'use strict';

var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};





function createCommonjsModule(fn, module) {
	return module = { exports: {} }, fn(module, module.exports), module.exports;
}

var jquery$1 = createCommonjsModule(function (module) {
	/*!
	 * jQuery JavaScript Library v3.2.1
	 * https://jquery.com/
	 *
	 * Includes Sizzle.js
	 * https://sizzlejs.com/
	 *
	 * Copyright JS Foundation and other contributors
	 * Released under the MIT license
	 * https://jquery.org/license
	 *
	 * Date: 2017-03-20T18:59Z
	 */
	(function (global, factory) {

		"use strict";

		{

			// For CommonJS and CommonJS-like environments where a proper `window`
			// is present, execute the factory and get jQuery.
			// For environments that do not have a `window` with a `document`
			// (such as Node.js), expose a factory as module.exports.
			// This accentuates the need for the creation of a real `window`.
			// e.g. var jQuery = require("jquery")(window);
			// See ticket #14549 for more info.
			module.exports = global.document ?
				factory(global, true) :
				function (w) {
					if (!w.document) {
						throw new Error("jQuery requires a window with a document");
					}
					return factory(w);
				};
		}

		// Pass this if window is not defined yet
	})(typeof window !== "undefined" ? window : commonjsGlobal, function (window, noGlobal) {

		// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
		// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
		// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
		// enough that all such attempts are guarded in a try block.
		"use strict";

		var arr = [];

		var document = window.document;

		var getProto = Object.getPrototypeOf;

		var slice = arr.slice;

		var concat = arr.concat;

		var push = arr.push;

		var indexOf = arr.indexOf;

		var class2type = {};

		var toString = class2type.toString;

		var hasOwn = class2type.hasOwnProperty;

		var fnToString = hasOwn.toString;

		var ObjectFunctionString = fnToString.call(Object);

		var support = {};



		function DOMEval(code, doc) {
			doc = doc || document;

			var script = doc.createElement("script");

			script.text = code;
			doc.head.appendChild(script).parentNode.removeChild(script);
		}
		/* global Symbol */
		// Defining this global in .eslintrc.json would create a danger of using the global
		// unguarded in another place, it seems safer to define global only for this module



		var
			version = "3.2.1",

			// Define a local copy of jQuery
			jQuery = function (selector, context) {

				// The jQuery object is actually just the init constructor 'enhanced'
				// Need init if jQuery is called (just allow error to be thrown if not included)
				return new jQuery.fn.init(selector, context);
			},

			// Support: Android <=4.0 only
			// Make sure we trim BOM and NBSP
			rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,

			// Matches dashed string for camelizing
			rmsPrefix = /^-ms-/,
			rdashAlpha = /-([a-z])/g,

			// Used by jQuery.camelCase as callback to replace()
			fcamelCase = function (all, letter) {
				return letter.toUpperCase();
			};

		jQuery.fn = jQuery.prototype = {

			// The current version of jQuery being used
			jquery: version,

			constructor: jQuery,

			// The default length of a jQuery object is 0
			length: 0,

			toArray: function () {
				return slice.call(this);
			},

			// Get the Nth element in the matched element set OR
			// Get the whole matched element set as a clean array
			get: function (num) {

				// Return all the elements in a clean array
				if (num == null) {
					return slice.call(this);
				}

				// Return just the one element from the set
				return num < 0 ? this[num + this.length] : this[num];
			},

			// Take an array of elements and push it onto the stack
			// (returning the new matched element set)
			pushStack: function (elems) {

				// Build a new jQuery matched element set
				var ret = jQuery.merge(this.constructor(), elems);

				// Add the old object onto the stack (as a reference)
				ret.prevObject = this;

				// Return the newly-formed element set
				return ret;
			},

			// Execute a callback for every element in the matched set.
			each: function (callback) {
				return jQuery.each(this, callback);
			},

			map: function (callback) {
				return this.pushStack(jQuery.map(this, function (elem, i) {
					return callback.call(elem, i, elem);
				}));
			},

			slice: function () {
				return this.pushStack(slice.apply(this, arguments));
			},

			first: function () {
				return this.eq(0);
			},

			last: function () {
				return this.eq(-1);
			},

			eq: function (i) {
				var len = this.length,
					j = +i + (i < 0 ? len : 0);
				return this.pushStack(j >= 0 && j < len ? [this[j]] : []);
			},

			end: function () {
				return this.prevObject || this.constructor();
			},

			// For internal use only.
			// Behaves like an Array's method, not like a jQuery method.
			push: push,
			sort: arr.sort,
			splice: arr.splice
		};

		jQuery.extend = jQuery.fn.extend = function () {
			var arguments$1 = arguments;

			var options, name, src, copy, copyIsArray, clone,
				target = arguments[0] || {},
				i = 1,
				length = arguments.length,
				deep = false;

			// Handle a deep copy situation
			if (typeof target === "boolean") {
				deep = target;

				// Skip the boolean and the target
				target = arguments[i] || {};
				i++;
			}

			// Handle case when target is a string or something (possible in deep copy)
			if (typeof target !== "object" && !jQuery.isFunction(target)) {
				target = {};
			}

			// Extend jQuery itself if only one argument is passed
			if (i === length) {
				target = this;
				i--;
			}

			for (; i < length; i++) {

				// Only deal with non-null/undefined values
				if ((options = arguments$1[i]) != null) {

					// Extend the base object
					for (name in options) {
						src = target[name];
						copy = options[name];

						// Prevent never-ending loop
						if (target === copy) {
							continue;
						}

						// Recurse if we're merging plain objects or arrays
						if (deep && copy && (jQuery.isPlainObject(copy) ||
							(copyIsArray = Array.isArray(copy)))) {

							if (copyIsArray) {
								copyIsArray = false;
								clone = src && Array.isArray(src) ? src : [];

							} else {
								clone = src && jQuery.isPlainObject(src) ? src : {};
							}

							// Never move original objects, clone them
							target[name] = jQuery.extend(deep, clone, copy);

							// Don't bring in undefined values
						} else if (copy !== undefined) {
							target[name] = copy;
						}
					}
				}
			}

			// Return the modified object
			return target;
		};

		jQuery.extend({

			// Unique for each copy of jQuery on the page
			expando: "jQuery" + (version + Math.random()).replace(/\D/g, ""),

			// Assume jQuery is ready without the ready module
			isReady: true,

			error: function (msg) {
				throw new Error(msg);
			},

			noop: function () { },

			isFunction: function (obj) {
				return jQuery.type(obj) === "function";
			},

			isWindow: function (obj) {
				return obj != null && obj === obj.window;
			},

			isNumeric: function (obj) {

				// As of jQuery 3.0, isNumeric is limited to
				// strings and numbers (primitives or objects)
				// that can be coerced to finite numbers (gh-2662)
				var type = jQuery.type(obj);
				return (type === "number" || type === "string") &&

					// parseFloat NaNs numeric-cast false positives ("")
					// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
					// subtraction forces infinities to NaN
					!isNaN(obj - parseFloat(obj));
			},

			isPlainObject: function (obj) {
				var proto, Ctor;

				// Detect obvious negatives
				// Use toString instead of jQuery.type to catch host objects
				if (!obj || toString.call(obj) !== "[object Object]") {
					return false;
				}

				proto = getProto(obj);

				// Objects with no prototype (e.g., `Object.create( null )`) are plain
				if (!proto) {
					return true;
				}

				// Objects with prototype are plain iff they were constructed by a global Object function
				Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
				return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
			},

			isEmptyObject: function (obj) {

				/* eslint-disable no-unused-vars */
				// See https://github.com/eslint/eslint/issues/6125
				var name;

				for (name in obj) {
					return false;
				}
				return true;
			},

			type: function (obj) {
				if (obj == null) {
					return obj + "";
				}

				// Support: Android <=2.3 only (functionish RegExp)
				return typeof obj === "object" || typeof obj === "function" ?
					class2type[toString.call(obj)] || "object" :
					typeof obj;
			},

			// Evaluates a script in a global context
			globalEval: function (code) {
				DOMEval(code);
			},

			// Convert dashed to camelCase; used by the css and data modules
			// Support: IE <=9 - 11, Edge 12 - 13
			// Microsoft forgot to hump their vendor prefix (#9572)
			camelCase: function (string) {
				return string.replace(rmsPrefix, "ms-").replace(rdashAlpha, fcamelCase);
			},

			each: function (obj, callback) {
				var length, i = 0;

				if (isArrayLike(obj)) {
					length = obj.length;
					for (; i < length; i++) {
						if (callback.call(obj[i], i, obj[i]) === false) {
							break;
						}
					}
				} else {
					for (i in obj) {
						if (callback.call(obj[i], i, obj[i]) === false) {
							break;
						}
					}
				}

				return obj;
			},

			// Support: Android <=4.0 only
			trim: function (text) {
				return text == null ?
					"" :
					(text + "").replace(rtrim, "");
			},

			// results is for internal usage only
			makeArray: function (arr, results) {
				var ret = results || [];

				if (arr != null) {
					if (isArrayLike(Object(arr))) {
						jQuery.merge(ret,
							typeof arr === "string" ?
								[arr] : arr
						);
					} else {
						push.call(ret, arr);
					}
				}

				return ret;
			},

			inArray: function (elem, arr, i) {
				return arr == null ? -1 : indexOf.call(arr, elem, i);
			},

			// Support: Android <=4.0 only, PhantomJS 1 only
			// push.apply(_, arraylike) throws on ancient WebKit
			merge: function (first, second) {
				var len = +second.length,
					j = 0,
					i = first.length;

				for (; j < len; j++) {
					first[i++] = second[j];
				}

				first.length = i;

				return first;
			},

			grep: function (elems, callback, invert) {
				var callbackInverse,
					matches = [],
					i = 0,
					length = elems.length,
					callbackExpect = !invert;

				// Go through the array, only saving the items
				// that pass the validator function
				for (; i < length; i++) {
					callbackInverse = !callback(elems[i], i);
					if (callbackInverse !== callbackExpect) {
						matches.push(elems[i]);
					}
				}

				return matches;
			},

			// arg is for internal usage only
			map: function (elems, callback, arg) {
				var length, value,
					i = 0,
					ret = [];

				// Go through the array, translating each of the items to their new values
				if (isArrayLike(elems)) {
					length = elems.length;
					for (; i < length; i++) {
						value = callback(elems[i], i, arg);

						if (value != null) {
							ret.push(value);
						}
					}

					// Go through every key on the object,
				} else {
					for (i in elems) {
						value = callback(elems[i], i, arg);

						if (value != null) {
							ret.push(value);
						}
					}
				}

				// Flatten any nested arrays
				return concat.apply([], ret);
			},

			// A global GUID counter for objects
			guid: 1,

			// Bind a function to a context, optionally partially applying any
			// arguments.
			proxy: function (fn, context) {
				var tmp, args, proxy;

				if (typeof context === "string") {
					tmp = fn[context];
					context = fn;
					fn = tmp;
				}

				// Quick check to determine if target is callable, in the spec
				// this throws a TypeError, but we will just return undefined.
				if (!jQuery.isFunction(fn)) {
					return undefined;
				}

				// Simulated bind
				args = slice.call(arguments, 2);
				proxy = function () {
					return fn.apply(context || this, args.concat(slice.call(arguments)));
				};

				// Set the guid of unique handler to the same of original handler, so it can be removed
				proxy.guid = fn.guid = fn.guid || jQuery.guid++;

				return proxy;
			},

			now: Date.now,

			// jQuery.support is not used in Core but other projects attach their
			// properties to it so it needs to exist.
			support: support
		});

		if (typeof Symbol === "function") {
			jQuery.fn[Symbol.iterator] = arr[Symbol.iterator];
		}

		// Populate the class2type map
		jQuery.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),
			function (i, name) {
				class2type["[object " + name + "]"] = name.toLowerCase();
			});

		function isArrayLike(obj) {

			// Support: real iOS 8.2 only (not reproducible in simulator)
			// `in` check used to prevent JIT error (gh-2145)
			// hasOwn isn't used here due to false negatives
			// regarding Nodelist length in IE
			var length = !!obj && "length" in obj && obj.length,
				type = jQuery.type(obj);

			if (type === "function" || jQuery.isWindow(obj)) {
				return false;
			}

			return type === "array" || length === 0 ||
				typeof length === "number" && length > 0 && (length - 1) in obj;
		}
		var Sizzle =
			/*!
			 * Sizzle CSS Selector Engine v2.3.3
			 * https://sizzlejs.com/
			 *
			 * Copyright jQuery Foundation and other contributors
			 * Released under the MIT license
			 * http://jquery.org/license
			 *
			 * Date: 2016-08-08
			 */
			(function (window) {

				var i,
					support,
					Expr,
					getText,
					isXML,
					tokenize,
					compile,
					select,
					outermostContext,
					sortInput,
					hasDuplicate,

					// Local document vars
					setDocument,
					document,
					docElem,
					documentIsHTML,
					rbuggyQSA,
					rbuggyMatches,
					matches,
					contains,

					// Instance-specific data
					expando = "sizzle" + 1 * new Date(),
					preferredDoc = window.document,
					dirruns = 0,
					done = 0,
					classCache = createCache(),
					tokenCache = createCache(),
					compilerCache = createCache(),
					sortOrder = function (a, b) {
						if (a === b) {
							hasDuplicate = true;
						}
						return 0;
					},

					// Instance methods
					hasOwn = ({}).hasOwnProperty,
					arr = [],
					pop = arr.pop,
					push_native = arr.push,
					push = arr.push,
					slice = arr.slice,
					// Use a stripped-down indexOf as it's faster than native
					// https://jsperf.com/thor-indexof-vs-for/5
					indexOf = function (list, elem) {
						var i = 0,
							len = list.length;
						for (; i < len; i++) {
							if (list[i] === elem) {
								return i;
							}
						}
						return -1;
					},

					booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",

					// Regular expressions

					// http://www.w3.org/TR/css3-selectors/#whitespace
					whitespace = "[\\x20\\t\\r\\n\\f]",

					// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
					identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+",

					// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
					attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
						// Operator (capture 2)
						"*([*^$|!~]?=)" + whitespace +
						// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
						"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
						"*\\]",

					pseudos = ":(" + identifier + ")(?:\\((" +
						// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
						// 1. quoted (capture 3; capture 4 or capture 5)
						"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
						// 2. simple (capture 6)
						"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
						// 3. anything else (capture 2)
						".*" +
						")\\)|)",

					// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
					rwhitespace = new RegExp(whitespace + "+", "g"),
					rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g"),

					rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"),
					rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*"),

					rattributeQuotes = new RegExp("=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g"),

					rpseudo = new RegExp(pseudos),
					ridentifier = new RegExp("^" + identifier + "$"),

					matchExpr = {
						"ID": new RegExp("^#(" + identifier + ")"),
						"CLASS": new RegExp("^\\.(" + identifier + ")"),
						"TAG": new RegExp("^(" + identifier + "|[*])"),
						"ATTR": new RegExp("^" + attributes),
						"PSEUDO": new RegExp("^" + pseudos),
						"CHILD": new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
							"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
							"*(\\d+)|))" + whitespace + "*\\)|)", "i"),
						"bool": new RegExp("^(?:" + booleans + ")$", "i"),
						// For use in libraries implementing .is()
						// We use this for POS matching in `select`
						"needsContext": new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
							whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i")
					},

					rinputs = /^(?:input|select|textarea|button)$/i,
					rheader = /^h\d$/i,

					rnative = /^[^{]+\{\s*\[native \w/,

					// Easily-parseable/retrievable ID or TAG or CLASS selectors
					rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,

					rsibling = /[+~]/,

					// CSS escapes
					// http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
					runescape = new RegExp("\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig"),
					funescape = function (_, escaped, escapedWhitespace) {
						var high = "0x" + escaped - 0x10000;
						// NaN means non-codepoint
						// Support: Firefox<24
						// Workaround erroneous numeric interpretation of +"0x"
						return high !== high || escapedWhitespace ?
							escaped :
							high < 0 ?
								// BMP codepoint
								String.fromCharCode(high + 0x10000) :
								// Supplemental Plane codepoint (surrogate pair)
								String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00);
					},

					// CSS string/identifier serialization
					// https://drafts.csswg.org/cssom/#common-serializing-idioms
					rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
					fcssescape = function (ch, asCodePoint) {
						if (asCodePoint) {

							// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
							if (ch === "\0") {
								return "\uFFFD";
							}

							// Control characters and (dependent upon position) numbers get escaped as code points
							return ch.slice(0, -1) + "\\" + ch.charCodeAt(ch.length - 1).toString(16) + " ";
						}

						// Other potentially-special ASCII characters get backslash-escaped
						return "\\" + ch;
					},

					// Used for iframes
					// See setDocument()
					// Removing the function wrapper causes a "Permission Denied"
					// error in IE
					unloadHandler = function () {
						setDocument();
					},

					disabledAncestor = addCombinator(
						function (elem) {
							return elem.disabled === true && ("form" in elem || "label" in elem);
						},
						{ dir: "parentNode", next: "legend" }
					);

				// Optimize for push.apply( _, NodeList )
				try {
					push.apply(
						(arr = slice.call(preferredDoc.childNodes)),
						preferredDoc.childNodes
					);
					// Support: Android<4.0
					// Detect silently failing push.apply
					arr[preferredDoc.childNodes.length].nodeType;
				} catch (e) {
					push = {
						apply: arr.length ?

							// Leverage slice if possible
							function (target, els) {
								push_native.apply(target, slice.call(els));
							} :

							// Support: IE<9
							// Otherwise append directly
							function (target, els) {
								var j = target.length,
									i = 0;
								// Can't trust NodeList.length
								while ((target[j++] = els[i++])) { }
								target.length = j - 1;
							}
					};
				}

				function Sizzle(selector, context, results, seed) {
					var m, i, elem, nid, match, groups, newSelector,
						newContext = context && context.ownerDocument,

						// nodeType defaults to 9, since context defaults to document
						nodeType = context ? context.nodeType : 9;

					results = results || [];

					// Return early from calls with invalid selector or context
					if (typeof selector !== "string" || !selector ||
						nodeType !== 1 && nodeType !== 9 && nodeType !== 11) {

						return results;
					}

					// Try to shortcut find operations (as opposed to filters) in HTML documents
					if (!seed) {

						if ((context ? context.ownerDocument || context : preferredDoc) !== document) {
							setDocument(context);
						}
						context = context || document;

						if (documentIsHTML) {

							// If the selector is sufficiently simple, try using a "get*By*" DOM method
							// (excepting DocumentFragment context, where the methods don't exist)
							if (nodeType !== 11 && (match = rquickExpr.exec(selector))) {

								// ID selector
								if ((m = match[1])) {

									// Document context
									if (nodeType === 9) {
										if ((elem = context.getElementById(m))) {

											// Support: IE, Opera, Webkit
											// TODO: identify versions
											// getElementById can match elements by name instead of ID
											if (elem.id === m) {
												results.push(elem);
												return results;
											}
										} else {
											return results;
										}

										// Element context
									} else {

										// Support: IE, Opera, Webkit
										// TODO: identify versions
										// getElementById can match elements by name instead of ID
										if (newContext && (elem = newContext.getElementById(m)) &&
											contains(context, elem) &&
											elem.id === m) {

											results.push(elem);
											return results;
										}
									}

									// Type selector
								} else if (match[2]) {
									push.apply(results, context.getElementsByTagName(selector));
									return results;

									// Class selector
								} else if ((m = match[3]) && support.getElementsByClassName &&
									context.getElementsByClassName) {

									push.apply(results, context.getElementsByClassName(m));
									return results;
								}
							}

							// Take advantage of querySelectorAll
							if (support.qsa &&
								!compilerCache[selector + " "] &&
								(!rbuggyQSA || !rbuggyQSA.test(selector))) {

								if (nodeType !== 1) {
									newContext = context;
									newSelector = selector;

									// qSA looks outside Element context, which is not what we want
									// Thanks to Andrew Dupont for this workaround technique
									// Support: IE <=8
									// Exclude object elements
								} else if (context.nodeName.toLowerCase() !== "object") {

									// Capture the context ID, setting it first if necessary
									if ((nid = context.getAttribute("id"))) {
										nid = nid.replace(rcssescape, fcssescape);
									} else {
										context.setAttribute("id", (nid = expando));
									}

									// Prefix every selector in the list
									groups = tokenize(selector);
									i = groups.length;
									while (i--) {
										groups[i] = "#" + nid + " " + toSelector(groups[i]);
									}
									newSelector = groups.join(",");

									// Expand context for sibling selectors
									newContext = rsibling.test(selector) && testContext(context.parentNode) ||
										context;
								}

								if (newSelector) {
									try {
										push.apply(results,
											newContext.querySelectorAll(newSelector)
										);
										return results;
									} catch (qsaError) {
									} finally {
										if (nid === expando) {
											context.removeAttribute("id");
										}
									}
								}
							}
						}
					}

					// All others
					return select(selector.replace(rtrim, "$1"), context, results, seed);
				}

				/**
				 * Create key-value caches of limited size
				 * @returns {function(string, object)} Returns the Object data after storing it on itself with
				 *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
				 *	deleting the oldest entry
				 */
				function createCache() {
					var keys = [];

					function cache(key, value) {
						// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
						if (keys.push(key + " ") > Expr.cacheLength) {
							// Only keep the most recent entries
							delete cache[keys.shift()];
						}
						return (cache[key + " "] = value);
					}
					return cache;
				}

				/**
				 * Mark a function for special use by Sizzle
				 * @param {Function} fn The function to mark
				 */
				function markFunction(fn) {
					fn[expando] = true;
					return fn;
				}

				/**
				 * Support testing using an element
				 * @param {Function} fn Passed the created element and returns a boolean result
				 */
				function assert(fn) {
					var el = document.createElement("fieldset");

					try {
						return !!fn(el);
					} catch (e) {
						return false;
					} finally {
						// Remove from its parent by default
						if (el.parentNode) {
							el.parentNode.removeChild(el);
						}
						// release memory in IE
						el = null;
					}
				}

				/**
				 * Adds the same handler for all of the specified attrs
				 * @param {String} attrs Pipe-separated list of attributes
				 * @param {Function} handler The method that will be applied
				 */
				function addHandle(attrs, handler) {
					var arr = attrs.split("|"),
						i = arr.length;

					while (i--) {
						Expr.attrHandle[arr[i]] = handler;
					}
				}

				/**
				 * Checks document order of two siblings
				 * @param {Element} a
				 * @param {Element} b
				 * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
				 */
				function siblingCheck(a, b) {
					var cur = b && a,
						diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
							a.sourceIndex - b.sourceIndex;

					// Use IE sourceIndex if available on both nodes
					if (diff) {
						return diff;
					}

					// Check if b follows a
					if (cur) {
						while ((cur = cur.nextSibling)) {
							if (cur === b) {
								return -1;
							}
						}
					}

					return a ? 1 : -1;
				}

				/**
				 * Returns a function to use in pseudos for input types
				 * @param {String} type
				 */
				function createInputPseudo(type) {
					return function (elem) {
						var name = elem.nodeName.toLowerCase();
						return name === "input" && elem.type === type;
					};
				}

				/**
				 * Returns a function to use in pseudos for buttons
				 * @param {String} type
				 */
				function createButtonPseudo(type) {
					return function (elem) {
						var name = elem.nodeName.toLowerCase();
						return (name === "input" || name === "button") && elem.type === type;
					};
				}

				/**
				 * Returns a function to use in pseudos for :enabled/:disabled
				 * @param {Boolean} disabled true for :disabled; false for :enabled
				 */
				function createDisabledPseudo(disabled) {

					// Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
					return function (elem) {

						// Only certain elements can match :enabled or :disabled
						// https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
						// https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
						if ("form" in elem) {

							// Check for inherited disabledness on relevant non-disabled elements:
							// * listed form-associated elements in a disabled fieldset
							//   https://html.spec.whatwg.org/multipage/forms.html#category-listed
							//   https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
							// * option elements in a disabled optgroup
							//   https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
							// All such elements have a "form" property.
							if (elem.parentNode && elem.disabled === false) {

								// Option elements defer to a parent optgroup if present
								if ("label" in elem) {
									if ("label" in elem.parentNode) {
										return elem.parentNode.disabled === disabled;
									} else {
										return elem.disabled === disabled;
									}
								}

								// Support: IE 6 - 11
								// Use the isDisabled shortcut property to check for disabled fieldset ancestors
								return elem.isDisabled === disabled ||

									// Where there is no isDisabled, check manually
									/* jshint -W018 */
									elem.isDisabled !== !disabled &&
									disabledAncestor(elem) === disabled;
							}

							return elem.disabled === disabled;

							// Try to winnow out elements that can't be disabled before trusting the disabled property.
							// Some victims get caught in our net (label, legend, menu, track), but it shouldn't
							// even exist on them, let alone have a boolean value.
						} else if ("label" in elem) {
							return elem.disabled === disabled;
						}

						// Remaining elements are neither :enabled nor :disabled
						return false;
					};
				}

				/**
				 * Returns a function to use in pseudos for positionals
				 * @param {Function} fn
				 */
				function createPositionalPseudo(fn) {
					return markFunction(function (argument) {
						argument = +argument;
						return markFunction(function (seed, matches) {
							var j,
								matchIndexes = fn([], seed.length, argument),
								i = matchIndexes.length;

							// Match elements found at the specified indexes
							while (i--) {
								if (seed[(j = matchIndexes[i])]) {
									seed[j] = !(matches[j] = seed[j]);
								}
							}
						});
					});
				}

				/**
				 * Checks a node for validity as a Sizzle context
				 * @param {Element|Object=} context
				 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
				 */
				function testContext(context) {
					return context && typeof context.getElementsByTagName !== "undefined" && context;
				}

				// Expose support vars for convenience
				support = Sizzle.support = {};

				/**
				 * Detects XML nodes
				 * @param {Element|Object} elem An element or a document
				 * @returns {Boolean} True iff elem is a non-HTML XML node
				 */
				isXML = Sizzle.isXML = function (elem) {
					// documentElement is verified for cases where it doesn't yet exist
					// (such as loading iframes in IE - #4833)
					var documentElement = elem && (elem.ownerDocument || elem).documentElement;
					return documentElement ? documentElement.nodeName !== "HTML" : false;
				};

				/**
				 * Sets document-related variables once based on the current document
				 * @param {Element|Object} [doc] An element or document object to use to set the document
				 * @returns {Object} Returns the current document
				 */
				setDocument = Sizzle.setDocument = function (node) {
					var hasCompare, subWindow,
						doc = node ? node.ownerDocument || node : preferredDoc;

					// Return early if doc is invalid or already selected
					if (doc === document || doc.nodeType !== 9 || !doc.documentElement) {
						return document;
					}

					// Update global variables
					document = doc;
					docElem = document.documentElement;
					documentIsHTML = !isXML(document);

					// Support: IE 9-11, Edge
					// Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
					if (preferredDoc !== document &&
						(subWindow = document.defaultView) && subWindow.top !== subWindow) {

						// Support: IE 11, Edge
						if (subWindow.addEventListener) {
							subWindow.addEventListener("unload", unloadHandler, false);

							// Support: IE 9 - 10 only
						} else if (subWindow.attachEvent) {
							subWindow.attachEvent("onunload", unloadHandler);
						}
					}

					/* Attributes
					---------------------------------------------------------------------- */

					// Support: IE<8
					// Verify that getAttribute really returns attributes and not properties
					// (excepting IE8 booleans)
					support.attributes = assert(function (el) {
						el.className = "i";
						return !el.getAttribute("className");
					});

					/* getElement(s)By*
					---------------------------------------------------------------------- */

					// Check if getElementsByTagName("*") returns only elements
					support.getElementsByTagName = assert(function (el) {
						el.appendChild(document.createComment(""));
						return !el.getElementsByTagName("*").length;
					});

					// Support: IE<9
					support.getElementsByClassName = rnative.test(document.getElementsByClassName);

					// Support: IE<10
					// Check if getElementById returns elements by name
					// The broken getElementById methods don't pick up programmatically-set names,
					// so use a roundabout getElementsByName test
					support.getById = assert(function (el) {
						docElem.appendChild(el).id = expando;
						return !document.getElementsByName || !document.getElementsByName(expando).length;
					});

					// ID filter and find
					if (support.getById) {
						Expr.filter["ID"] = function (id) {
							var attrId = id.replace(runescape, funescape);
							return function (elem) {
								return elem.getAttribute("id") === attrId;
							};
						};
						Expr.find["ID"] = function (id, context) {
							if (typeof context.getElementById !== "undefined" && documentIsHTML) {
								var elem = context.getElementById(id);
								return elem ? [elem] : [];
							}
						};
					} else {
						Expr.filter["ID"] = function (id) {
							var attrId = id.replace(runescape, funescape);
							return function (elem) {
								var node = typeof elem.getAttributeNode !== "undefined" &&
									elem.getAttributeNode("id");
								return node && node.value === attrId;
							};
						};

						// Support: IE 6 - 7 only
						// getElementById is not reliable as a find shortcut
						Expr.find["ID"] = function (id, context) {
							if (typeof context.getElementById !== "undefined" && documentIsHTML) {
								var node, i, elems,
									elem = context.getElementById(id);

								if (elem) {

									// Verify the id attribute
									node = elem.getAttributeNode("id");
									if (node && node.value === id) {
										return [elem];
									}

									// Fall back on getElementsByName
									elems = context.getElementsByName(id);
									i = 0;
									while ((elem = elems[i++])) {
										node = elem.getAttributeNode("id");
										if (node && node.value === id) {
											return [elem];
										}
									}
								}

								return [];
							}
						};
					}

					// Tag
					Expr.find["TAG"] = support.getElementsByTagName ?
						function (tag, context) {
							if (typeof context.getElementsByTagName !== "undefined") {
								return context.getElementsByTagName(tag);

								// DocumentFragment nodes don't have gEBTN
							} else if (support.qsa) {
								return context.querySelectorAll(tag);
							}
						} :

						function (tag, context) {
							var elem,
								tmp = [],
								i = 0,
								// By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
								results = context.getElementsByTagName(tag);

							// Filter out possible comments
							if (tag === "*") {
								while ((elem = results[i++])) {
									if (elem.nodeType === 1) {
										tmp.push(elem);
									}
								}

								return tmp;
							}
							return results;
						};

					// Class
					Expr.find["CLASS"] = support.getElementsByClassName && function (className, context) {
						if (typeof context.getElementsByClassName !== "undefined" && documentIsHTML) {
							return context.getElementsByClassName(className);
						}
					};

					/* QSA/matchesSelector
					---------------------------------------------------------------------- */

					// QSA and matchesSelector support

					// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
					rbuggyMatches = [];

					// qSa(:focus) reports false when true (Chrome 21)
					// We allow this because of a bug in IE8/9 that throws an error
					// whenever `document.activeElement` is accessed on an iframe
					// So, we allow :focus to pass through QSA all the time to avoid the IE error
					// See https://bugs.jquery.com/ticket/13378
					rbuggyQSA = [];

					if ((support.qsa = rnative.test(document.querySelectorAll))) {
						// Build QSA regex
						// Regex strategy adopted from Diego Perini
						assert(function (el) {
							// Select is set to empty string on purpose
							// This is to test IE's treatment of not explicitly
							// setting a boolean content attribute,
							// since its presence should be enough
							// https://bugs.jquery.com/ticket/12359
							docElem.appendChild(el).innerHTML = "<a id='" + expando + "'></a>" +
								"<select id='" + expando + "-\r\\' msallowcapture=''>" +
								"<option selected=''></option></select>";

							// Support: IE8, Opera 11-12.16
							// Nothing should be selected when empty strings follow ^= or $= or *=
							// The test attribute must be unknown in Opera but "safe" for WinRT
							// https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
							if (el.querySelectorAll("[msallowcapture^='']").length) {
								rbuggyQSA.push("[*^$]=" + whitespace + "*(?:''|\"\")");
							}

							// Support: IE8
							// Boolean attributes and "value" are not treated correctly
							if (!el.querySelectorAll("[selected]").length) {
								rbuggyQSA.push("\\[" + whitespace + "*(?:value|" + booleans + ")");
							}

							// Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
							if (!el.querySelectorAll("[id~=" + expando + "-]").length) {
								rbuggyQSA.push("~=");
							}

							// Webkit/Opera - :checked should return selected option elements
							// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
							// IE8 throws error here and will not see later tests
							if (!el.querySelectorAll(":checked").length) {
								rbuggyQSA.push(":checked");
							}

							// Support: Safari 8+, iOS 8+
							// https://bugs.webkit.org/show_bug.cgi?id=136851
							// In-page `selector#id sibling-combinator selector` fails
							if (!el.querySelectorAll("a#" + expando + "+*").length) {
								rbuggyQSA.push(".#.+[+~]");
							}
						});

						assert(function (el) {
							el.innerHTML = "<a href='' disabled='disabled'></a>" +
								"<select disabled='disabled'><option/></select>";

							// Support: Windows 8 Native Apps
							// The type and name attributes are restricted during .innerHTML assignment
							var input = document.createElement("input");
							input.setAttribute("type", "hidden");
							el.appendChild(input).setAttribute("name", "D");

							// Support: IE8
							// Enforce case-sensitivity of name attribute
							if (el.querySelectorAll("[name=d]").length) {
								rbuggyQSA.push("name" + whitespace + "*[*^$|!~]?=");
							}

							// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
							// IE8 throws error here and will not see later tests
							if (el.querySelectorAll(":enabled").length !== 2) {
								rbuggyQSA.push(":enabled", ":disabled");
							}

							// Support: IE9-11+
							// IE's :disabled selector does not pick up the children of disabled fieldsets
							docElem.appendChild(el).disabled = true;
							if (el.querySelectorAll(":disabled").length !== 2) {
								rbuggyQSA.push(":enabled", ":disabled");
							}

							// Opera 10-11 does not throw on post-comma invalid pseudos
							el.querySelectorAll("*,:x");
							rbuggyQSA.push(",.*:");
						});
					}

					if ((support.matchesSelector = rnative.test((matches = docElem.matches ||
						docElem.webkitMatchesSelector ||
						docElem.mozMatchesSelector ||
						docElem.oMatchesSelector ||
						docElem.msMatchesSelector)))) {

						assert(function (el) {
							// Check to see if it's possible to do matchesSelector
							// on a disconnected node (IE 9)
							support.disconnectedMatch = matches.call(el, "*");

							// This should fail with an exception
							// Gecko does not error, returns false instead
							matches.call(el, "[s!='']:x");
							rbuggyMatches.push("!=", pseudos);
						});
					}

					rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join("|"));
					rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join("|"));

					/* Contains
					---------------------------------------------------------------------- */
					hasCompare = rnative.test(docElem.compareDocumentPosition);

					// Element contains another
					// Purposefully self-exclusive
					// As in, an element does not contain itself
					contains = hasCompare || rnative.test(docElem.contains) ?
						function (a, b) {
							var adown = a.nodeType === 9 ? a.documentElement : a,
								bup = b && b.parentNode;
							return a === bup || !!(bup && bup.nodeType === 1 && (
								adown.contains ?
									adown.contains(bup) :
									a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16
							));
						} :
						function (a, b) {
							if (b) {
								while ((b = b.parentNode)) {
									if (b === a) {
										return true;
									}
								}
							}
							return false;
						};

					/* Sorting
					---------------------------------------------------------------------- */

					// Document order sorting
					sortOrder = hasCompare ?
						function (a, b) {

							// Flag for duplicate removal
							if (a === b) {
								hasDuplicate = true;
								return 0;
							}

							// Sort on method existence if only one input has compareDocumentPosition
							var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
							if (compare) {
								return compare;
							}

							// Calculate position if both inputs belong to the same document
							compare = (a.ownerDocument || a) === (b.ownerDocument || b) ?
								a.compareDocumentPosition(b) :

								// Otherwise we know they are disconnected
								1;

							// Disconnected nodes
							if (compare & 1 ||
								(!support.sortDetached && b.compareDocumentPosition(a) === compare)) {

								// Choose the first element that is related to our preferred document
								if (a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a)) {
									return -1;
								}
								if (b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b)) {
									return 1;
								}

								// Maintain original order
								return sortInput ?
									(indexOf(sortInput, a) - indexOf(sortInput, b)) :
									0;
							}

							return compare & 4 ? -1 : 1;
						} :
						function (a, b) {
							// Exit early if the nodes are identical
							if (a === b) {
								hasDuplicate = true;
								return 0;
							}

							var cur,
								i = 0,
								aup = a.parentNode,
								bup = b.parentNode,
								ap = [a],
								bp = [b];

							// Parentless nodes are either documents or disconnected
							if (!aup || !bup) {
								return a === document ? -1 :
									b === document ? 1 :
										aup ? -1 :
											bup ? 1 :
												sortInput ?
													(indexOf(sortInput, a) - indexOf(sortInput, b)) :
													0;

								// If the nodes are siblings, we can do a quick check
							} else if (aup === bup) {
								return siblingCheck(a, b);
							}

							// Otherwise we need full lists of their ancestors for comparison
							cur = a;
							while ((cur = cur.parentNode)) {
								ap.unshift(cur);
							}
							cur = b;
							while ((cur = cur.parentNode)) {
								bp.unshift(cur);
							}

							// Walk down the tree looking for a discrepancy
							while (ap[i] === bp[i]) {
								i++;
							}

							return i ?
								// Do a sibling check if the nodes have a common ancestor
								siblingCheck(ap[i], bp[i]) :

								// Otherwise nodes in our document sort first
								ap[i] === preferredDoc ? -1 :
									bp[i] === preferredDoc ? 1 :
										0;
						};

					return document;
				};

				Sizzle.matches = function (expr, elements) {
					return Sizzle(expr, null, null, elements);
				};

				Sizzle.matchesSelector = function (elem, expr) {
					// Set document vars if needed
					if ((elem.ownerDocument || elem) !== document) {
						setDocument(elem);
					}

					// Make sure that attribute selectors are quoted
					expr = expr.replace(rattributeQuotes, "='$1']");

					if (support.matchesSelector && documentIsHTML &&
						!compilerCache[expr + " "] &&
						(!rbuggyMatches || !rbuggyMatches.test(expr)) &&
						(!rbuggyQSA || !rbuggyQSA.test(expr))) {

						try {
							var ret = matches.call(elem, expr);

							// IE 9's matchesSelector returns false on disconnected nodes
							if (ret || support.disconnectedMatch ||
								// As well, disconnected nodes are said to be in a document
								// fragment in IE 9
								elem.document && elem.document.nodeType !== 11) {
								return ret;
							}
						} catch (e) { }
					}

					return Sizzle(expr, document, null, [elem]).length > 0;
				};

				Sizzle.contains = function (context, elem) {
					// Set document vars if needed
					if ((context.ownerDocument || context) !== document) {
						setDocument(context);
					}
					return contains(context, elem);
				};

				Sizzle.attr = function (elem, name) {
					// Set document vars if needed
					if ((elem.ownerDocument || elem) !== document) {
						setDocument(elem);
					}

					var fn = Expr.attrHandle[name.toLowerCase()],
						// Don't get fooled by Object.prototype properties (jQuery #13807)
						val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) ?
							fn(elem, name, !documentIsHTML) :
							undefined;

					return val !== undefined ?
						val :
						support.attributes || !documentIsHTML ?
							elem.getAttribute(name) :
							(val = elem.getAttributeNode(name)) && val.specified ?
								val.value :
								null;
				};

				Sizzle.escape = function (sel) {
					return (sel + "").replace(rcssescape, fcssescape);
				};

				Sizzle.error = function (msg) {
					throw new Error("Syntax error, unrecognized expression: " + msg);
				};

				/**
				 * Document sorting and removing duplicates
				 * @param {ArrayLike} results
				 */
				Sizzle.uniqueSort = function (results) {
					var elem,
						duplicates = [],
						j = 0,
						i = 0;

					// Unless we *know* we can detect duplicates, assume their presence
					hasDuplicate = !support.detectDuplicates;
					sortInput = !support.sortStable && results.slice(0);
					results.sort(sortOrder);

					if (hasDuplicate) {
						while ((elem = results[i++])) {
							if (elem === results[i]) {
								j = duplicates.push(i);
							}
						}
						while (j--) {
							results.splice(duplicates[j], 1);
						}
					}

					// Clear input after sorting to release objects
					// See https://github.com/jquery/sizzle/pull/225
					sortInput = null;

					return results;
				};

				/**
				 * Utility function for retrieving the text value of an array of DOM nodes
				 * @param {Array|Element} elem
				 */
				getText = Sizzle.getText = function (elem) {
					var node,
						ret = "",
						i = 0,
						nodeType = elem.nodeType;

					if (!nodeType) {
						// If no nodeType, this is expected to be an array
						while ((node = elem[i++])) {
							// Do not traverse comment nodes
							ret += getText(node);
						}
					} else if (nodeType === 1 || nodeType === 9 || nodeType === 11) {
						// Use textContent for elements
						// innerText usage removed for consistency of new lines (jQuery #11153)
						if (typeof elem.textContent === "string") {
							return elem.textContent;
						} else {
							// Traverse its children
							for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
								ret += getText(elem);
							}
						}
					} else if (nodeType === 3 || nodeType === 4) {
						return elem.nodeValue;
					}
					// Do not include comment or processing instruction nodes

					return ret;
				};

				Expr = Sizzle.selectors = {

					// Can be adjusted by the user
					cacheLength: 50,

					createPseudo: markFunction,

					match: matchExpr,

					attrHandle: {},

					find: {},

					relative: {
						">": { dir: "parentNode", first: true },
						" ": { dir: "parentNode" },
						"+": { dir: "previousSibling", first: true },
						"~": { dir: "previousSibling" }
					},

					preFilter: {
						"ATTR": function (match) {
							match[1] = match[1].replace(runescape, funescape);

							// Move the given value to match[3] whether quoted or unquoted
							match[3] = (match[3] || match[4] || match[5] || "").replace(runescape, funescape);

							if (match[2] === "~=") {
								match[3] = " " + match[3] + " ";
							}

							return match.slice(0, 4);
						},

						"CHILD": function (match) {
							/* matches from matchExpr["CHILD"]
								1 type (only|nth|...)
								2 what (child|of-type)
								3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
								4 xn-component of xn+y argument ([+-]?\d*n|)
								5 sign of xn-component
								6 x of xn-component
								7 sign of y-component
								8 y of y-component
							*/
							match[1] = match[1].toLowerCase();

							if (match[1].slice(0, 3) === "nth") {
								// nth-* requires argument
								if (!match[3]) {
									Sizzle.error(match[0]);
								}

								// numeric x and y parameters for Expr.filter.CHILD
								// remember that false/true cast respectively to 0/1
								match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === "even" || match[3] === "odd"));
								match[5] = +((match[7] + match[8]) || match[3] === "odd");

								// other types prohibit arguments
							} else if (match[3]) {
								Sizzle.error(match[0]);
							}

							return match;
						},

						"PSEUDO": function (match) {
							var excess,
								unquoted = !match[6] && match[2];

							if (matchExpr["CHILD"].test(match[0])) {
								return null;
							}

							// Accept quoted arguments as-is
							if (match[3]) {
								match[2] = match[4] || match[5] || "";

								// Strip excess characters from unquoted arguments
							} else if (unquoted && rpseudo.test(unquoted) &&
								// Get excess from tokenize (recursively)
								(excess = tokenize(unquoted, true)) &&
								// advance to the next closing parenthesis
								(excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)) {

								// excess is a negative index
								match[0] = match[0].slice(0, excess);
								match[2] = unquoted.slice(0, excess);
							}

							// Return only captures needed by the pseudo filter method (type and argument)
							return match.slice(0, 3);
						}
					},

					filter: {

						"TAG": function (nodeNameSelector) {
							var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase();
							return nodeNameSelector === "*" ?
								function () { return true; } :
								function (elem) {
									return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
								};
						},

						"CLASS": function (className) {
							var pattern = classCache[className + " "];

							return pattern ||
								(pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) &&
								classCache(className, function (elem) {
									return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "");
								});
						},

						"ATTR": function (name, operator, check) {
							return function (elem) {
								var result = Sizzle.attr(elem, name);

								if (result == null) {
									return operator === "!=";
								}
								if (!operator) {
									return true;
								}

								result += "";

								return operator === "=" ? result === check :
									operator === "!=" ? result !== check :
										operator === "^=" ? check && result.indexOf(check) === 0 :
											operator === "*=" ? check && result.indexOf(check) > -1 :
												operator === "$=" ? check && result.slice(-check.length) === check :
													operator === "~=" ? (" " + result.replace(rwhitespace, " ") + " ").indexOf(check) > -1 :
														operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" :
															false;
							};
						},

						"CHILD": function (type, what, argument, first, last) {
							var simple = type.slice(0, 3) !== "nth",
								forward = type.slice(-4) !== "last",
								ofType = what === "of-type";

							return first === 1 && last === 0 ?

								// Shortcut for :nth-*(n)
								function (elem) {
									return !!elem.parentNode;
								} :

								function (elem, context, xml) {
									var cache, uniqueCache, outerCache, node, nodeIndex, start,
										dir = simple !== forward ? "nextSibling" : "previousSibling",
										parent = elem.parentNode,
										name = ofType && elem.nodeName.toLowerCase(),
										useCache = !xml && !ofType,
										diff = false;

									if (parent) {

										// :(first|last|only)-(child|of-type)
										if (simple) {
											while (dir) {
												node = elem;
												while ((node = node[dir])) {
													if (ofType ?
														node.nodeName.toLowerCase() === name :
														node.nodeType === 1) {

														return false;
													}
												}
												// Reverse direction for :only-* (if we haven't yet done so)
												start = dir = type === "only" && !start && "nextSibling";
											}
											return true;
										}

										start = [forward ? parent.firstChild : parent.lastChild];

										// non-xml :nth-child(...) stores cache data on `parent`
										if (forward && useCache) {

											// Seek `elem` from a previously-cached index

											// ...in a gzip-friendly way
											node = parent;
											outerCache = node[expando] || (node[expando] = {});

											// Support: IE <9 only
											// Defend against cloned attroperties (jQuery gh-1709)
											uniqueCache = outerCache[node.uniqueID] ||
												(outerCache[node.uniqueID] = {});

											cache = uniqueCache[type] || [];
											nodeIndex = cache[0] === dirruns && cache[1];
											diff = nodeIndex && cache[2];
											node = nodeIndex && parent.childNodes[nodeIndex];

											while ((node = ++nodeIndex && node && node[dir] ||

												// Fallback to seeking `elem` from the start
												(diff = nodeIndex = 0) || start.pop())) {

												// When found, cache indexes on `parent` and break
												if (node.nodeType === 1 && ++diff && node === elem) {
													uniqueCache[type] = [dirruns, nodeIndex, diff];
													break;
												}
											}

										} else {
											// Use previously-cached element index if available
											if (useCache) {
												// ...in a gzip-friendly way
												node = elem;
												outerCache = node[expando] || (node[expando] = {});

												// Support: IE <9 only
												// Defend against cloned attroperties (jQuery gh-1709)
												uniqueCache = outerCache[node.uniqueID] ||
													(outerCache[node.uniqueID] = {});

												cache = uniqueCache[type] || [];
												nodeIndex = cache[0] === dirruns && cache[1];
												diff = nodeIndex;
											}

											// xml :nth-child(...)
											// or :nth-last-child(...) or :nth(-last)?-of-type(...)
											if (diff === false) {
												// Use the same loop as above to seek `elem` from the start
												while ((node = ++nodeIndex && node && node[dir] ||
													(diff = nodeIndex = 0) || start.pop())) {

													if ((ofType ?
														node.nodeName.toLowerCase() === name :
														node.nodeType === 1) &&
														++diff) {

														// Cache the index of each encountered element
														if (useCache) {
															outerCache = node[expando] || (node[expando] = {});

															// Support: IE <9 only
															// Defend against cloned attroperties (jQuery gh-1709)
															uniqueCache = outerCache[node.uniqueID] ||
																(outerCache[node.uniqueID] = {});

															uniqueCache[type] = [dirruns, diff];
														}

														if (node === elem) {
															break;
														}
													}
												}
											}
										}

										// Incorporate the offset, then check against cycle size
										diff -= last;
										return diff === first || (diff % first === 0 && diff / first >= 0);
									}
								};
						},

						"PSEUDO": function (pseudo, argument) {
							// pseudo-class names are case-insensitive
							// http://www.w3.org/TR/selectors/#pseudo-classes
							// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
							// Remember that setFilters inherits from pseudos
							var args,
								fn = Expr.pseudos[pseudo] || Expr.setFilters[pseudo.toLowerCase()] ||
									Sizzle.error("unsupported pseudo: " + pseudo);

							// The user may use createPseudo to indicate that
							// arguments are needed to create the filter function
							// just as Sizzle does
							if (fn[expando]) {
								return fn(argument);
							}

							// But maintain support for old signatures
							if (fn.length > 1) {
								args = [pseudo, pseudo, "", argument];
								return Expr.setFilters.hasOwnProperty(pseudo.toLowerCase()) ?
									markFunction(function (seed, matches) {
										var idx,
											matched = fn(seed, argument),
											i = matched.length;
										while (i--) {
											idx = indexOf(seed, matched[i]);
											seed[idx] = !(matches[idx] = matched[i]);
										}
									}) :
									function (elem) {
										return fn(elem, 0, args);
									};
							}

							return fn;
						}
					},

					pseudos: {
						// Potentially complex pseudos
						"not": markFunction(function (selector) {
							// Trim the selector passed to compile
							// to avoid treating leading and trailing
							// spaces as combinators
							var input = [],
								results = [],
								matcher = compile(selector.replace(rtrim, "$1"));

							return matcher[expando] ?
								markFunction(function (seed, matches, context, xml) {
									var elem,
										unmatched = matcher(seed, null, xml, []),
										i = seed.length;

									// Match elements unmatched by `matcher`
									while (i--) {
										if ((elem = unmatched[i])) {
											seed[i] = !(matches[i] = elem);
										}
									}
								}) :
								function (elem, context, xml) {
									input[0] = elem;
									matcher(input, null, xml, results);
									// Don't keep the element (issue #299)
									input[0] = null;
									return !results.pop();
								};
						}),

						"has": markFunction(function (selector) {
							return function (elem) {
								return Sizzle(selector, elem).length > 0;
							};
						}),

						"contains": markFunction(function (text) {
							text = text.replace(runescape, funescape);
							return function (elem) {
								return (elem.textContent || elem.innerText || getText(elem)).indexOf(text) > -1;
							};
						}),

						// "Whether an element is represented by a :lang() selector
						// is based solely on the element's language value
						// being equal to the identifier C,
						// or beginning with the identifier C immediately followed by "-".
						// The matching of C against the element's language value is performed case-insensitively.
						// The identifier C does not have to be a valid language name."
						// http://www.w3.org/TR/selectors/#lang-pseudo
						"lang": markFunction(function (lang) {
							// lang value must be a valid identifier
							if (!ridentifier.test(lang || "")) {
								Sizzle.error("unsupported lang: " + lang);
							}
							lang = lang.replace(runescape, funescape).toLowerCase();
							return function (elem) {
								var elemLang;
								do {
									if ((elemLang = documentIsHTML ?
										elem.lang :
										elem.getAttribute("xml:lang") || elem.getAttribute("lang"))) {

										elemLang = elemLang.toLowerCase();
										return elemLang === lang || elemLang.indexOf(lang + "-") === 0;
									}
								} while ((elem = elem.parentNode) && elem.nodeType === 1);
								return false;
							};
						}),

						// Miscellaneous
						"target": function (elem) {
							var hash = window.location && window.location.hash;
							return hash && hash.slice(1) === elem.id;
						},

						"root": function (elem) {
							return elem === docElem;
						},

						"focus": function (elem) {
							return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
						},

						// Boolean properties
						"enabled": createDisabledPseudo(false),
						"disabled": createDisabledPseudo(true),

						"checked": function (elem) {
							// In CSS3, :checked should return both checked and selected elements
							// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
							var nodeName = elem.nodeName.toLowerCase();
							return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
						},

						"selected": function (elem) {
							// Accessing this property makes selected-by-default
							// options in Safari work properly
							if (elem.parentNode) {
								elem.parentNode.selectedIndex;
							}

							return elem.selected === true;
						},

						// Contents
						"empty": function (elem) {
							// http://www.w3.org/TR/selectors/#empty-pseudo
							// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
							//   but not by others (comment: 8; processing instruction: 7; etc.)
							// nodeType < 6 works because attributes (2) do not appear as children
							for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
								if (elem.nodeType < 6) {
									return false;
								}
							}
							return true;
						},

						"parent": function (elem) {
							return !Expr.pseudos["empty"](elem);
						},

						// Element/input types
						"header": function (elem) {
							return rheader.test(elem.nodeName);
						},

						"input": function (elem) {
							return rinputs.test(elem.nodeName);
						},

						"button": function (elem) {
							var name = elem.nodeName.toLowerCase();
							return name === "input" && elem.type === "button" || name === "button";
						},

						"text": function (elem) {
							var attr;
							return elem.nodeName.toLowerCase() === "input" &&
								elem.type === "text" &&

								// Support: IE<8
								// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
								((attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text");
						},

						// Position-in-collection
						"first": createPositionalPseudo(function () {
							return [0];
						}),

						"last": createPositionalPseudo(function (matchIndexes, length) {
							return [length - 1];
						}),

						"eq": createPositionalPseudo(function (matchIndexes, length, argument) {
							return [argument < 0 ? argument + length : argument];
						}),

						"even": createPositionalPseudo(function (matchIndexes, length) {
							var i = 0;
							for (; i < length; i += 2) {
								matchIndexes.push(i);
							}
							return matchIndexes;
						}),

						"odd": createPositionalPseudo(function (matchIndexes, length) {
							var i = 1;
							for (; i < length; i += 2) {
								matchIndexes.push(i);
							}
							return matchIndexes;
						}),

						"lt": createPositionalPseudo(function (matchIndexes, length, argument) {
							var i = argument < 0 ? argument + length : argument;
							for (; --i >= 0;) {
								matchIndexes.push(i);
							}
							return matchIndexes;
						}),

						"gt": createPositionalPseudo(function (matchIndexes, length, argument) {
							var i = argument < 0 ? argument + length : argument;
							for (; ++i < length;) {
								matchIndexes.push(i);
							}
							return matchIndexes;
						})
					}
				};

				Expr.pseudos["nth"] = Expr.pseudos["eq"];

				// Add button/input type pseudos
				for (i in { radio: true, checkbox: true, file: true, password: true, image: true }) {
					Expr.pseudos[i] = createInputPseudo(i);
				}
				for (i in { submit: true, reset: true }) {
					Expr.pseudos[i] = createButtonPseudo(i);
				}

				// Easy API for creating new setFilters
				function setFilters() { }
				setFilters.prototype = Expr.filters = Expr.pseudos;
				Expr.setFilters = new setFilters();

				tokenize = Sizzle.tokenize = function (selector, parseOnly) {
					var matched, match, tokens, type,
						soFar, groups, preFilters,
						cached = tokenCache[selector + " "];

					if (cached) {
						return parseOnly ? 0 : cached.slice(0);
					}

					soFar = selector;
					groups = [];
					preFilters = Expr.preFilter;

					while (soFar) {

						// Comma and first run
						if (!matched || (match = rcomma.exec(soFar))) {
							if (match) {
								// Don't consume trailing commas as valid
								soFar = soFar.slice(match[0].length) || soFar;
							}
							groups.push((tokens = []));
						}

						matched = false;

						// Combinators
						if ((match = rcombinators.exec(soFar))) {
							matched = match.shift();
							tokens.push({
								value: matched,
								// Cast descendant combinators to space
								type: match[0].replace(rtrim, " ")
							});
							soFar = soFar.slice(matched.length);
						}

						// Filters
						for (type in Expr.filter) {
							if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] ||
								(match = preFilters[type](match)))) {
								matched = match.shift();
								tokens.push({
									value: matched,
									type: type,
									matches: match
								});
								soFar = soFar.slice(matched.length);
							}
						}

						if (!matched) {
							break;
						}
					}

					// Return the length of the invalid excess
					// if we're just parsing
					// Otherwise, throw an error or return tokens
					return parseOnly ?
						soFar.length :
						soFar ?
							Sizzle.error(selector) :
							// Cache the tokens
							tokenCache(selector, groups).slice(0);
				};

				function toSelector(tokens) {
					var i = 0,
						len = tokens.length,
						selector = "";
					for (; i < len; i++) {
						selector += tokens[i].value;
					}
					return selector;
				}

				function addCombinator(matcher, combinator, base) {
					var dir = combinator.dir,
						skip = combinator.next,
						key = skip || dir,
						checkNonElements = base && key === "parentNode",
						doneName = done++;

					return combinator.first ?
						// Check against closest ancestor/preceding element
						function (elem, context, xml) {
							while ((elem = elem[dir])) {
								if (elem.nodeType === 1 || checkNonElements) {
									return matcher(elem, context, xml);
								}
							}
							return false;
						} :

						// Check against all ancestor/preceding elements
						function (elem, context, xml) {
							var oldCache, uniqueCache, outerCache,
								newCache = [dirruns, doneName];

							// We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
							if (xml) {
								while ((elem = elem[dir])) {
									if (elem.nodeType === 1 || checkNonElements) {
										if (matcher(elem, context, xml)) {
											return true;
										}
									}
								}
							} else {
								while ((elem = elem[dir])) {
									if (elem.nodeType === 1 || checkNonElements) {
										outerCache = elem[expando] || (elem[expando] = {});

										// Support: IE <9 only
										// Defend against cloned attroperties (jQuery gh-1709)
										uniqueCache = outerCache[elem.uniqueID] || (outerCache[elem.uniqueID] = {});

										if (skip && skip === elem.nodeName.toLowerCase()) {
											elem = elem[dir] || elem;
										} else if ((oldCache = uniqueCache[key]) &&
											oldCache[0] === dirruns && oldCache[1] === doneName) {

											// Assign to newCache so results back-propagate to previous elements
											return (newCache[2] = oldCache[2]);
										} else {
											// Reuse newcache so results back-propagate to previous elements
											uniqueCache[key] = newCache;

											// A match means we're done; a fail means we have to keep checking
											if ((newCache[2] = matcher(elem, context, xml))) {
												return true;
											}
										}
									}
								}
							}
							return false;
						};
				}

				function elementMatcher(matchers) {
					return matchers.length > 1 ?
						function (elem, context, xml) {
							var i = matchers.length;
							while (i--) {
								if (!matchers[i](elem, context, xml)) {
									return false;
								}
							}
							return true;
						} :
						matchers[0];
				}

				function multipleContexts(selector, contexts, results) {
					var i = 0,
						len = contexts.length;
					for (; i < len; i++) {
						Sizzle(selector, contexts[i], results);
					}
					return results;
				}

				function condense(unmatched, map, filter, context, xml) {
					var elem,
						newUnmatched = [],
						i = 0,
						len = unmatched.length,
						mapped = map != null;

					for (; i < len; i++) {
						if ((elem = unmatched[i])) {
							if (!filter || filter(elem, context, xml)) {
								newUnmatched.push(elem);
								if (mapped) {
									map.push(i);
								}
							}
						}
					}

					return newUnmatched;
				}

				function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector) {
					if (postFilter && !postFilter[expando]) {
						postFilter = setMatcher(postFilter);
					}
					if (postFinder && !postFinder[expando]) {
						postFinder = setMatcher(postFinder, postSelector);
					}
					return markFunction(function (seed, results, context, xml) {
						var temp, i, elem,
							preMap = [],
							postMap = [],
							preexisting = results.length,

							// Get initial elements from seed or context
							elems = seed || multipleContexts(selector || "*", context.nodeType ? [context] : context, []),

							// Prefilter to get matcher input, preserving a map for seed-results synchronization
							matcherIn = preFilter && (seed || !selector) ?
								condense(elems, preMap, preFilter, context, xml) :
								elems,

							matcherOut = matcher ?
								// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
								postFinder || (seed ? preFilter : preexisting || postFilter) ?

									// ...intermediate processing is necessary
									[] :

									// ...otherwise use results directly
									results :
								matcherIn;

						// Find primary matches
						if (matcher) {
							matcher(matcherIn, matcherOut, context, xml);
						}

						// Apply postFilter
						if (postFilter) {
							temp = condense(matcherOut, postMap);
							postFilter(temp, [], context, xml);

							// Un-match failing elements by moving them back to matcherIn
							i = temp.length;
							while (i--) {
								if ((elem = temp[i])) {
									matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem);
								}
							}
						}

						if (seed) {
							if (postFinder || preFilter) {
								if (postFinder) {
									// Get the final matcherOut by condensing this intermediate into postFinder contexts
									temp = [];
									i = matcherOut.length;
									while (i--) {
										if ((elem = matcherOut[i])) {
											// Restore matcherIn since elem is not yet a final match
											temp.push((matcherIn[i] = elem));
										}
									}
									postFinder(null, (matcherOut = []), temp, xml);
								}

								// Move matched elements from seed to results to keep them synchronized
								i = matcherOut.length;
								while (i--) {
									if ((elem = matcherOut[i]) &&
										(temp = postFinder ? indexOf(seed, elem) : preMap[i]) > -1) {

										seed[temp] = !(results[temp] = elem);
									}
								}
							}

							// Add elements to results, through postFinder if defined
						} else {
							matcherOut = condense(
								matcherOut === results ?
									matcherOut.splice(preexisting, matcherOut.length) :
									matcherOut
							);
							if (postFinder) {
								postFinder(null, results, matcherOut, xml);
							} else {
								push.apply(results, matcherOut);
							}
						}
					});
				}

				function matcherFromTokens(tokens) {
					var checkContext, matcher, j,
						len = tokens.length,
						leadingRelative = Expr.relative[tokens[0].type],
						implicitRelative = leadingRelative || Expr.relative[" "],
						i = leadingRelative ? 1 : 0,

						// The foundational matcher ensures that elements are reachable from top-level context(s)
						matchContext = addCombinator(function (elem) {
							return elem === checkContext;
						}, implicitRelative, true),
						matchAnyContext = addCombinator(function (elem) {
							return indexOf(checkContext, elem) > -1;
						}, implicitRelative, true),
						matchers = [function (elem, context, xml) {
							var ret = (!leadingRelative && (xml || context !== outermostContext)) || (
								(checkContext = context).nodeType ?
									matchContext(elem, context, xml) :
									matchAnyContext(elem, context, xml));
							// Avoid hanging onto element (issue #299)
							checkContext = null;
							return ret;
						}];

					for (; i < len; i++) {
						if ((matcher = Expr.relative[tokens[i].type])) {
							matchers = [addCombinator(elementMatcher(matchers), matcher)];
						} else {
							matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches);

							// Return special upon seeing a positional matcher
							if (matcher[expando]) {
								// Find the next relative operator (if any) for proper handling
								j = ++i;
								for (; j < len; j++) {
									if (Expr.relative[tokens[j].type]) {
										break;
									}
								}
								return setMatcher(
									i > 1 && elementMatcher(matchers),
									i > 1 && toSelector(
										// If the preceding token was a descendant combinator, insert an implicit any-element `*`
										tokens.slice(0, i - 1).concat({ value: tokens[i - 2].type === " " ? "*" : "" })
									).replace(rtrim, "$1"),
									matcher,
									i < j && matcherFromTokens(tokens.slice(i, j)),
									j < len && matcherFromTokens((tokens = tokens.slice(j))),
									j < len && toSelector(tokens)
								);
							}
							matchers.push(matcher);
						}
					}

					return elementMatcher(matchers);
				}

				function matcherFromGroupMatchers(elementMatchers, setMatchers) {
					var bySet = setMatchers.length > 0,
						byElement = elementMatchers.length > 0,
						superMatcher = function (seed, context, xml, results, outermost) {
							var elem, j, matcher,
								matchedCount = 0,
								i = "0",
								unmatched = seed && [],
								setMatched = [],
								contextBackup = outermostContext,
								// We must always have either seed elements or outermost context
								elems = seed || byElement && Expr.find["TAG"]("*", outermost),
								// Use integer dirruns iff this is the outermost matcher
								dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
								len = elems.length;

							if (outermost) {
								outermostContext = context === document || context || outermost;
							}

							// Add elements passing elementMatchers directly to results
							// Support: IE<9, Safari
							// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
							for (; i !== len && (elem = elems[i]) != null; i++) {
								if (byElement && elem) {
									j = 0;
									if (!context && elem.ownerDocument !== document) {
										setDocument(elem);
										xml = !documentIsHTML;
									}
									while ((matcher = elementMatchers[j++])) {
										if (matcher(elem, context || document, xml)) {
											results.push(elem);
											break;
										}
									}
									if (outermost) {
										dirruns = dirrunsUnique;
									}
								}

								// Track unmatched elements for set filters
								if (bySet) {
									// They will have gone through all possible matchers
									if ((elem = !matcher && elem)) {
										matchedCount--;
									}

									// Lengthen the array for every element, matched or not
									if (seed) {
										unmatched.push(elem);
									}
								}
							}

							// `i` is now the count of elements visited above, and adding it to `matchedCount`
							// makes the latter nonnegative.
							matchedCount += i;

							// Apply set filters to unmatched elements
							// NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
							// equals `i`), unless we didn't visit _any_ elements in the above loop because we have
							// no element matchers and no seed.
							// Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
							// case, which will result in a "00" `matchedCount` that differs from `i` but is also
							// numerically zero.
							if (bySet && i !== matchedCount) {
								j = 0;
								while ((matcher = setMatchers[j++])) {
									matcher(unmatched, setMatched, context, xml);
								}

								if (seed) {
									// Reintegrate element matches to eliminate the need for sorting
									if (matchedCount > 0) {
										while (i--) {
											if (!(unmatched[i] || setMatched[i])) {
												setMatched[i] = pop.call(results);
											}
										}
									}

									// Discard index placeholder values to get only actual matches
									setMatched = condense(setMatched);
								}

								// Add matches to results
								push.apply(results, setMatched);

								// Seedless set matches succeeding multiple successful matchers stipulate sorting
								if (outermost && !seed && setMatched.length > 0 &&
									(matchedCount + setMatchers.length) > 1) {

									Sizzle.uniqueSort(results);
								}
							}

							// Override manipulation of globals by nested matchers
							if (outermost) {
								dirruns = dirrunsUnique;
								outermostContext = contextBackup;
							}

							return unmatched;
						};

					return bySet ?
						markFunction(superMatcher) :
						superMatcher;
				}

				compile = Sizzle.compile = function (selector, match /* Internal Use Only */) {
					var i,
						setMatchers = [],
						elementMatchers = [],
						cached = compilerCache[selector + " "];

					if (!cached) {
						// Generate a function of recursive functions that can be used to check each element
						if (!match) {
							match = tokenize(selector);
						}
						i = match.length;
						while (i--) {
							cached = matcherFromTokens(match[i]);
							if (cached[expando]) {
								setMatchers.push(cached);
							} else {
								elementMatchers.push(cached);
							}
						}

						// Cache the compiled function
						cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers));

						// Save selector and tokenization
						cached.selector = selector;
					}
					return cached;
				};

				/**
				 * A low-level selection function that works with Sizzle's compiled
				 *  selector functions
				 * @param {String|Function} selector A selector or a pre-compiled
				 *  selector function built with Sizzle.compile
				 * @param {Element} context
				 * @param {Array} [results]
				 * @param {Array} [seed] A set of elements to match against
				 */
				select = Sizzle.select = function (selector, context, results, seed) {
					var i, tokens, token, type, find,
						compiled = typeof selector === "function" && selector,
						match = !seed && tokenize((selector = compiled.selector || selector));

					results = results || [];

					// Try to minimize operations if there is only one selector in the list and no seed
					// (the latter of which guarantees us context)
					if (match.length === 1) {

						// Reduce context if the leading compound selector is an ID
						tokens = match[0] = match[0].slice(0);
						if (tokens.length > 2 && (token = tokens[0]).type === "ID" &&
							context.nodeType === 9 && documentIsHTML && Expr.relative[tokens[1].type]) {

							context = (Expr.find["ID"](token.matches[0].replace(runescape, funescape), context) || [])[0];
							if (!context) {
								return results;

								// Precompiled matchers will still verify ancestry, so step up a level
							} else if (compiled) {
								context = context.parentNode;
							}

							selector = selector.slice(tokens.shift().value.length);
						}

						// Fetch a seed set for right-to-left matching
						i = matchExpr["needsContext"].test(selector) ? 0 : tokens.length;
						while (i--) {
							token = tokens[i];

							// Abort if we hit a combinator
							if (Expr.relative[(type = token.type)]) {
								break;
							}
							if ((find = Expr.find[type])) {
								// Search, expanding context for leading sibling combinators
								if ((seed = find(
									token.matches[0].replace(runescape, funescape),
									rsibling.test(tokens[0].type) && testContext(context.parentNode) || context
								))) {

									// If seed is empty or no tokens remain, we can return early
									tokens.splice(i, 1);
									selector = seed.length && toSelector(tokens);
									if (!selector) {
										push.apply(results, seed);
										return results;
									}

									break;
								}
							}
						}
					}

					// Compile and execute a filtering function if one is not provided
					// Provide `match` to avoid retokenization if we modified the selector above
					(compiled || compile(selector, match))(
						seed,
						context,
						!documentIsHTML,
						results,
						!context || rsibling.test(selector) && testContext(context.parentNode) || context
					);
					return results;
				};

				// One-time assignments

				// Sort stability
				support.sortStable = expando.split("").sort(sortOrder).join("") === expando;

				// Support: Chrome 14-35+
				// Always assume duplicates if they aren't passed to the comparison function
				support.detectDuplicates = !!hasDuplicate;

				// Initialize against the default document
				setDocument();

				// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
				// Detached nodes confoundingly follow *each other*
				support.sortDetached = assert(function (el) {
					// Should return 1, but returns 4 (following)
					return el.compareDocumentPosition(document.createElement("fieldset")) & 1;
				});

				// Support: IE<8
				// Prevent attribute/property "interpolation"
				// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
				if (!assert(function (el) {
					el.innerHTML = "<a href='#'></a>";
					return el.firstChild.getAttribute("href") === "#";
				})) {
					addHandle("type|href|height|width", function (elem, name, isXML) {
						if (!isXML) {
							return elem.getAttribute(name, name.toLowerCase() === "type" ? 1 : 2);
						}
					});
				}

				// Support: IE<9
				// Use defaultValue in place of getAttribute("value")
				if (!support.attributes || !assert(function (el) {
					el.innerHTML = "<input/>";
					el.firstChild.setAttribute("value", "");
					return el.firstChild.getAttribute("value") === "";
				})) {
					addHandle("value", function (elem, name, isXML) {
						if (!isXML && elem.nodeName.toLowerCase() === "input") {
							return elem.defaultValue;
						}
					});
				}

				// Support: IE<9
				// Use getAttributeNode to fetch booleans when getAttribute lies
				if (!assert(function (el) {
					return el.getAttribute("disabled") == null;
				})) {
					addHandle(booleans, function (elem, name, isXML) {
						var val;
						if (!isXML) {
							return elem[name] === true ? name.toLowerCase() :
								(val = elem.getAttributeNode(name)) && val.specified ?
									val.value :
									null;
						}
					});
				}

				return Sizzle;

			})(window);



		jQuery.find = Sizzle;
		jQuery.expr = Sizzle.selectors;

		// Deprecated
		jQuery.expr[":"] = jQuery.expr.pseudos;
		jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
		jQuery.text = Sizzle.getText;
		jQuery.isXMLDoc = Sizzle.isXML;
		jQuery.contains = Sizzle.contains;
		jQuery.escapeSelector = Sizzle.escape;




		var dir = function (elem, dir, until) {
			var matched = [],
				truncate = until !== undefined;

			while ((elem = elem[dir]) && elem.nodeType !== 9) {
				if (elem.nodeType === 1) {
					if (truncate && jQuery(elem).is(until)) {
						break;
					}
					matched.push(elem);
				}
			}
			return matched;
		};


		var siblings = function (n, elem) {
			var matched = [];

			for (; n; n = n.nextSibling) {
				if (n.nodeType === 1 && n !== elem) {
					matched.push(n);
				}
			}

			return matched;
		};


		var rneedsContext = jQuery.expr.match.needsContext;



		function nodeName(elem, name) {

			return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();

		}
		var rsingleTag = (/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i);



		var risSimple = /^.[^:#\[\.,]*$/;

		// Implement the identical functionality for filter and not
		function winnow(elements, qualifier, not) {
			if (jQuery.isFunction(qualifier)) {
				return jQuery.grep(elements, function (elem, i) {
					return !!qualifier.call(elem, i, elem) !== not;
				});
			}

			// Single element
			if (qualifier.nodeType) {
				return jQuery.grep(elements, function (elem) {
					return (elem === qualifier) !== not;
				});
			}

			// Arraylike of elements (jQuery, arguments, Array)
			if (typeof qualifier !== "string") {
				return jQuery.grep(elements, function (elem) {
					return (indexOf.call(qualifier, elem) > -1) !== not;
				});
			}

			// Simple selector that can be filtered directly, removing non-Elements
			if (risSimple.test(qualifier)) {
				return jQuery.filter(qualifier, elements, not);
			}

			// Complex selector, compare the two sets, removing non-Elements
			qualifier = jQuery.filter(qualifier, elements);
			return jQuery.grep(elements, function (elem) {
				return (indexOf.call(qualifier, elem) > -1) !== not && elem.nodeType === 1;
			});
		}

		jQuery.filter = function (expr, elems, not) {
			var elem = elems[0];

			if (not) {
				expr = ":not(" + expr + ")";
			}

			if (elems.length === 1 && elem.nodeType === 1) {
				return jQuery.find.matchesSelector(elem, expr) ? [elem] : [];
			}

			return jQuery.find.matches(expr, jQuery.grep(elems, function (elem) {
				return elem.nodeType === 1;
			}));
		};

		jQuery.fn.extend({
			find: function (selector) {
				var i, ret,
					len = this.length,
					self = this;

				if (typeof selector !== "string") {
					return this.pushStack(jQuery(selector).filter(function () {
						var this$1 = this;

						for (i = 0; i < len; i++) {
							if (jQuery.contains(self[i], this$1)) {
								return true;
							}
						}
					}));
				}

				ret = this.pushStack([]);

				for (i = 0; i < len; i++) {
					jQuery.find(selector, self[i], ret);
				}

				return len > 1 ? jQuery.uniqueSort(ret) : ret;
			},
			filter: function (selector) {
				return this.pushStack(winnow(this, selector || [], false));
			},
			not: function (selector) {
				return this.pushStack(winnow(this, selector || [], true));
			},
			is: function (selector) {
				return !!winnow(
					this,

					// If this is a positional/relative selector, check membership in the returned set
					// so $("p:first").is("p:last") won't return true for a doc with two "p".
					typeof selector === "string" && rneedsContext.test(selector) ?
						jQuery(selector) :
						selector || [],
					false
				).length;
			}
		});


		// Initialize a jQuery object


		// A central reference to the root jQuery(document)
		var rootjQuery,

			// A simple way to check for HTML strings
			// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
			// Strict HTML recognition (#11290: must start with <)
			// Shortcut simple #id case for speed
			rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,

			init = jQuery.fn.init = function (selector, context, root) {
				var this$1 = this;

				var match, elem;

				// HANDLE: $(""), $(null), $(undefined), $(false)
				if (!selector) {
					return this;
				}

				// Method init() accepts an alternate rootjQuery
				// so migrate can support jQuery.sub (gh-2101)
				root = root || rootjQuery;

				// Handle HTML strings
				if (typeof selector === "string") {
					if (selector[0] === "<" &&
						selector[selector.length - 1] === ">" &&
						selector.length >= 3) {

						// Assume that strings that start and end with <> are HTML and skip the regex check
						match = [null, selector, null];

					} else {
						match = rquickExpr.exec(selector);
					}

					// Match html or make sure no context is specified for #id
					if (match && (match[1] || !context)) {

						// HANDLE: $(html) -> $(array)
						if (match[1]) {
							context = context instanceof jQuery ? context[0] : context;

							// Option to run scripts is true for back-compat
							// Intentionally let the error be thrown if parseHTML is not present
							jQuery.merge(this, jQuery.parseHTML(
								match[1],
								context && context.nodeType ? context.ownerDocument || context : document,
								true
							));

							// HANDLE: $(html, props)
							if (rsingleTag.test(match[1]) && jQuery.isPlainObject(context)) {
								for (match in context) {

									// Properties of context are called as methods if possible
									if (jQuery.isFunction(this$1[match])) {
										this$1[match](context[match]);

										// ...and otherwise set as attributes
									} else {
										this$1.attr(match, context[match]);
									}
								}
							}

							return this;

							// HANDLE: $(#id)
						} else {
							elem = document.getElementById(match[2]);

							if (elem) {

								// Inject the element directly into the jQuery object
								this[0] = elem;
								this.length = 1;
							}
							return this;
						}

						// HANDLE: $(expr, $(...))
					} else if (!context || context.jquery) {
						return (context || root).find(selector);

						// HANDLE: $(expr, context)
						// (which is just equivalent to: $(context).find(expr)
					} else {
						return this.constructor(context).find(selector);
					}

					// HANDLE: $(DOMElement)
				} else if (selector.nodeType) {
					this[0] = selector;
					this.length = 1;
					return this;

					// HANDLE: $(function)
					// Shortcut for document ready
				} else if (jQuery.isFunction(selector)) {
					return root.ready !== undefined ?
						root.ready(selector) :

						// Execute immediately if ready is not present
						selector(jQuery);
				}

				return jQuery.makeArray(selector, this);
			};

		// Give the init function the jQuery prototype for later instantiation
		init.prototype = jQuery.fn;

		// Initialize central reference
		rootjQuery = jQuery(document);


		var rparentsprev = /^(?:parents|prev(?:Until|All))/,

			// Methods guaranteed to produce a unique set when starting from a unique set
			guaranteedUnique = {
				children: true,
				contents: true,
				next: true,
				prev: true
			};

		jQuery.fn.extend({
			has: function (target) {
				var targets = jQuery(target, this),
					l = targets.length;

				return this.filter(function () {
					var this$1 = this;

					var i = 0;
					for (; i < l; i++) {
						if (jQuery.contains(this$1, targets[i])) {
							return true;
						}
					}
				});
			},

			closest: function (selectors, context) {
				var cur,
					i = 0,
					l = this.length,
					matched = [],
					targets = typeof selectors !== "string" && jQuery(selectors);

				// Positional selectors never match, since there's no _selection_ context
				if (!rneedsContext.test(selectors)) {
					for (; i < l; i++) {
						for (cur = this[i]; cur && cur !== context; cur = cur.parentNode) {

							// Always skip document fragments
							if (cur.nodeType < 11 && (targets ?
								targets.index(cur) > -1 :

								// Don't pass non-elements to Sizzle
								cur.nodeType === 1 &&
								jQuery.find.matchesSelector(cur, selectors))) {

								matched.push(cur);
								break;
							}
						}
					}
				}

				return this.pushStack(matched.length > 1 ? jQuery.uniqueSort(matched) : matched);
			},

			// Determine the position of an element within the set
			index: function (elem) {

				// No argument, return index in parent
				if (!elem) {
					return (this[0] && this[0].parentNode) ? this.first().prevAll().length : -1;
				}

				// Index in selector
				if (typeof elem === "string") {
					return indexOf.call(jQuery(elem), this[0]);
				}

				// Locate the position of the desired element
				return indexOf.call(this,

					// If it receives a jQuery object, the first element is used
					elem.jquery ? elem[0] : elem
				);
			},

			add: function (selector, context) {
				return this.pushStack(
					jQuery.uniqueSort(
						jQuery.merge(this.get(), jQuery(selector, context))
					)
				);
			},

			addBack: function (selector) {
				return this.add(selector == null ?
					this.prevObject : this.prevObject.filter(selector)
				);
			}
		});

		function sibling(cur, dir) {
			while ((cur = cur[dir]) && cur.nodeType !== 1) { }
			return cur;
		}

		jQuery.each({
			parent: function (elem) {
				var parent = elem.parentNode;
				return parent && parent.nodeType !== 11 ? parent : null;
			},
			parents: function (elem) {
				return dir(elem, "parentNode");
			},
			parentsUntil: function (elem, i, until) {
				return dir(elem, "parentNode", until);
			},
			next: function (elem) {
				return sibling(elem, "nextSibling");
			},
			prev: function (elem) {
				return sibling(elem, "previousSibling");
			},
			nextAll: function (elem) {
				return dir(elem, "nextSibling");
			},
			prevAll: function (elem) {
				return dir(elem, "previousSibling");
			},
			nextUntil: function (elem, i, until) {
				return dir(elem, "nextSibling", until);
			},
			prevUntil: function (elem, i, until) {
				return dir(elem, "previousSibling", until);
			},
			siblings: function (elem) {
				return siblings((elem.parentNode || {}).firstChild, elem);
			},
			children: function (elem) {
				return siblings(elem.firstChild);
			},
			contents: function (elem) {
				if (nodeName(elem, "iframe")) {
					return elem.contentDocument;
				}

				// Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
				// Treat the template element as a regular one in browsers that
				// don't support it.
				if (nodeName(elem, "template")) {
					elem = elem.content || elem;
				}

				return jQuery.merge([], elem.childNodes);
			}
		}, function (name, fn) {
			jQuery.fn[name] = function (until, selector) {
				var matched = jQuery.map(this, fn, until);

				if (name.slice(-5) !== "Until") {
					selector = until;
				}

				if (selector && typeof selector === "string") {
					matched = jQuery.filter(selector, matched);
				}

				if (this.length > 1) {

					// Remove duplicates
					if (!guaranteedUnique[name]) {
						jQuery.uniqueSort(matched);
					}

					// Reverse order for parents* and prev-derivatives
					if (rparentsprev.test(name)) {
						matched.reverse();
					}
				}

				return this.pushStack(matched);
			};
		});
		var rnothtmlwhite = (/[^\x20\t\r\n\f]+/g);



		// Convert String-formatted options into Object-formatted ones
		function createOptions(options) {
			var object = {};
			jQuery.each(options.match(rnothtmlwhite) || [], function (_, flag) {
				object[flag] = true;
			});
			return object;
		}

		/*
		 * Create a callback list using the following parameters:
		 *
		 *	options: an optional list of space-separated options that will change how
		 *			the callback list behaves or a more traditional option object
		 *
		 * By default a callback list will act like an event callback list and can be
		 * "fired" multiple times.
		 *
		 * Possible options:
		 *
		 *	once:			will ensure the callback list can only be fired once (like a Deferred)
		 *
		 *	memory:			will keep track of previous values and will call any callback added
		 *					after the list has been fired right away with the latest "memorized"
		 *					values (like a Deferred)
		 *
		 *	unique:			will ensure a callback can only be added once (no duplicate in the list)
		 *
		 *	stopOnFalse:	interrupt callings when a callback returns false
		 *
		 */
		jQuery.Callbacks = function (options) {

			// Convert options from String-formatted to Object-formatted if needed
			// (we check in cache first)
			options = typeof options === "string" ?
				createOptions(options) :
				jQuery.extend({}, options);

			var // Flag to know if list is currently firing
				firing,

				// Last fire value for non-forgettable lists
				memory,

				// Flag to know if list was already fired
				fired,

				// Flag to prevent firing
				locked,

				// Actual callback list
				list = [],

				// Queue of execution data for repeatable lists
				queue = [],

				// Index of currently firing callback (modified by add/remove as needed)
				firingIndex = -1,

				// Fire callbacks
				fire = function () {

					// Enforce single-firing
					locked = locked || options.once;

					// Execute callbacks for all pending executions,
					// respecting firingIndex overrides and runtime changes
					fired = firing = true;
					for (; queue.length; firingIndex = -1) {
						memory = queue.shift();
						while (++firingIndex < list.length) {

							// Run callback and check for early termination
							if (list[firingIndex].apply(memory[0], memory[1]) === false &&
								options.stopOnFalse) {

								// Jump to end and forget the data so .add doesn't re-fire
								firingIndex = list.length;
								memory = false;
							}
						}
					}

					// Forget the data if we're done with it
					if (!options.memory) {
						memory = false;
					}

					firing = false;

					// Clean up if we're done firing for good
					if (locked) {

						// Keep an empty list if we have data for future add calls
						if (memory) {
							list = [];

							// Otherwise, this object is spent
						} else {
							list = "";
						}
					}
				},

				// Actual Callbacks object
				self = {

					// Add a callback or a collection of callbacks to the list
					add: function () {
						if (list) {

							// If we have memory from a past run, we should fire after adding
							if (memory && !firing) {
								firingIndex = list.length - 1;
								queue.push(memory);
							}

							(function add(args) {
								jQuery.each(args, function (_, arg) {
									if (jQuery.isFunction(arg)) {
										if (!options.unique || !self.has(arg)) {
											list.push(arg);
										}
									} else if (arg && arg.length && jQuery.type(arg) !== "string") {

										// Inspect recursively
										add(arg);
									}
								});
							})(arguments);

							if (memory && !firing) {
								fire();
							}
						}
						return this;
					},

					// Remove a callback from the list
					remove: function () {
						jQuery.each(arguments, function (_, arg) {
							var index;
							while ((index = jQuery.inArray(arg, list, index)) > -1) {
								list.splice(index, 1);

								// Handle firing indexes
								if (index <= firingIndex) {
									firingIndex--;
								}
							}
						});
						return this;
					},

					// Check if a given callback is in the list.
					// If no argument is given, return whether or not list has callbacks attached.
					has: function (fn) {
						return fn ?
							jQuery.inArray(fn, list) > -1 :
							list.length > 0;
					},

					// Remove all callbacks from the list
					empty: function () {
						if (list) {
							list = [];
						}
						return this;
					},

					// Disable .fire and .add
					// Abort any current/pending executions
					// Clear all callbacks and values
					disable: function () {
						locked = queue = [];
						list = memory = "";
						return this;
					},
					disabled: function () {
						return !list;
					},

					// Disable .fire
					// Also disable .add unless we have memory (since it would have no effect)
					// Abort any pending executions
					lock: function () {
						locked = queue = [];
						if (!memory && !firing) {
							list = memory = "";
						}
						return this;
					},
					locked: function () {
						return !!locked;
					},

					// Call all callbacks with the given context and arguments
					fireWith: function (context, args) {
						if (!locked) {
							args = args || [];
							args = [context, args.slice ? args.slice() : args];
							queue.push(args);
							if (!firing) {
								fire();
							}
						}
						return this;
					},

					// Call all the callbacks with the given arguments
					fire: function () {
						self.fireWith(this, arguments);
						return this;
					},

					// To know if the callbacks have already been called at least once
					fired: function () {
						return !!fired;
					}
				};

			return self;
		};


		function Identity(v) {
			return v;
		}
		function Thrower(ex) {
			throw ex;
		}

		function adoptValue(value, resolve, reject, noValue) {
			var method;

			try {

				// Check for promise aspect first to privilege synchronous behavior
				if (value && jQuery.isFunction((method = value.promise))) {
					method.call(value).done(resolve).fail(reject);

					// Other thenables
				} else if (value && jQuery.isFunction((method = value.then))) {
					method.call(value, resolve, reject);

					// Other non-thenables
				} else {

					// Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
					// * false: [ value ].slice( 0 ) => resolve( value )
					// * true: [ value ].slice( 1 ) => resolve()
					resolve.apply(undefined, [value].slice(noValue));
				}

				// For Promises/A+, convert exceptions into rejections
				// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
				// Deferred#then to conditionally suppress rejection.
			} catch (value) {

				// Support: Android 4.0 only
				// Strict mode functions invoked without .call/.apply get global-object context
				reject.apply(undefined, [value]);
			}
		}

		jQuery.extend({

			Deferred: function (func) {
				var tuples = [

					// action, add listener, callbacks,
					// ... .then handlers, argument index, [final state]
					["notify", "progress", jQuery.Callbacks("memory"),
						jQuery.Callbacks("memory"), 2],
					["resolve", "done", jQuery.Callbacks("once memory"),
						jQuery.Callbacks("once memory"), 0, "resolved"],
					["reject", "fail", jQuery.Callbacks("once memory"),
						jQuery.Callbacks("once memory"), 1, "rejected"]
				],
					state = "pending",
					promise = {
						state: function () {
							return state;
						},
						always: function () {
							deferred.done(arguments).fail(arguments);
							return this;
						},
						"catch": function (fn) {
							return promise.then(null, fn);
						},

						// Keep pipe for back-compat
						pipe: function ( /* fnDone, fnFail, fnProgress */) {
							var fns = arguments;

							return jQuery.Deferred(function (newDefer) {
								jQuery.each(tuples, function (i, tuple) {

									// Map tuples (progress, done, fail) to arguments (done, fail, progress)
									var fn = jQuery.isFunction(fns[tuple[4]]) && fns[tuple[4]];

									// deferred.progress(function() { bind to newDefer or newDefer.notify })
									// deferred.done(function() { bind to newDefer or newDefer.resolve })
									// deferred.fail(function() { bind to newDefer or newDefer.reject })
									deferred[tuple[1]](function () {
										var returned = fn && fn.apply(this, arguments);
										if (returned && jQuery.isFunction(returned.promise)) {
											returned.promise()
												.progress(newDefer.notify)
												.done(newDefer.resolve)
												.fail(newDefer.reject);
										} else {
											newDefer[tuple[0] + "With"](
												this,
												fn ? [returned] : arguments
											);
										}
									});
								});
								fns = null;
							}).promise();
						},
						then: function (onFulfilled, onRejected, onProgress) {
							var maxDepth = 0;
							function resolve(depth, deferred, handler, special) {
								return function () {
									var that = this,
										args = arguments,
										mightThrow = function () {
											var returned, then;

											// Support: Promises/A+ section 2.3.3.3.3
											// https://promisesaplus.com/#point-59
											// Ignore double-resolution attempts
											if (depth < maxDepth) {
												return;
											}

											returned = handler.apply(that, args);

											// Support: Promises/A+ section 2.3.1
											// https://promisesaplus.com/#point-48
											if (returned === deferred.promise()) {
												throw new TypeError("Thenable self-resolution");
											}

											// Support: Promises/A+ sections 2.3.3.1, 3.5
											// https://promisesaplus.com/#point-54
											// https://promisesaplus.com/#point-75
											// Retrieve `then` only once
											then = returned &&

												// Support: Promises/A+ section 2.3.4
												// https://promisesaplus.com/#point-64
												// Only check objects and functions for thenability
												(typeof returned === "object" ||
													typeof returned === "function") &&
												returned.then;

											// Handle a returned thenable
											if (jQuery.isFunction(then)) {

												// Special processors (notify) just wait for resolution
												if (special) {
													then.call(
														returned,
														resolve(maxDepth, deferred, Identity, special),
														resolve(maxDepth, deferred, Thrower, special)
													);

													// Normal processors (resolve) also hook into progress
												} else {

													// ...and disregard older resolution values
													maxDepth++;

													then.call(
														returned,
														resolve(maxDepth, deferred, Identity, special),
														resolve(maxDepth, deferred, Thrower, special),
														resolve(maxDepth, deferred, Identity,
															deferred.notifyWith)
													);
												}

												// Handle all other returned values
											} else {

												// Only substitute handlers pass on context
												// and multiple values (non-spec behavior)
												if (handler !== Identity) {
													that = undefined;
													args = [returned];
												}

												// Process the value(s)
												// Default process is resolve
												(special || deferred.resolveWith)(that, args);
											}
										},

										// Only normal processors (resolve) catch and reject exceptions
										process = special ?
											mightThrow :
											function () {
												try {
													mightThrow();
												} catch (e) {

													if (jQuery.Deferred.exceptionHook) {
														jQuery.Deferred.exceptionHook(e,
															process.stackTrace);
													}

													// Support: Promises/A+ section 2.3.3.3.4.1
													// https://promisesaplus.com/#point-61
													// Ignore post-resolution exceptions
													if (depth + 1 >= maxDepth) {

														// Only substitute handlers pass on context
														// and multiple values (non-spec behavior)
														if (handler !== Thrower) {
															that = undefined;
															args = [e];
														}

														deferred.rejectWith(that, args);
													}
												}
											};

									// Support: Promises/A+ section 2.3.3.3.1
									// https://promisesaplus.com/#point-57
									// Re-resolve promises immediately to dodge false rejection from
									// subsequent errors
									if (depth) {
										process();
									} else {

										// Call an optional hook to record the stack, in case of exception
										// since it's otherwise lost when execution goes async
										if (jQuery.Deferred.getStackHook) {
											process.stackTrace = jQuery.Deferred.getStackHook();
										}
										window.setTimeout(process);
									}
								};
							}

							return jQuery.Deferred(function (newDefer) {

								// progress_handlers.add( ... )
								tuples[0][3].add(
									resolve(
										0,
										newDefer,
										jQuery.isFunction(onProgress) ?
											onProgress :
											Identity,
										newDefer.notifyWith
									)
								);

								// fulfilled_handlers.add( ... )
								tuples[1][3].add(
									resolve(
										0,
										newDefer,
										jQuery.isFunction(onFulfilled) ?
											onFulfilled :
											Identity
									)
								);

								// rejected_handlers.add( ... )
								tuples[2][3].add(
									resolve(
										0,
										newDefer,
										jQuery.isFunction(onRejected) ?
											onRejected :
											Thrower
									)
								);
							}).promise();
						},

						// Get a promise for this deferred
						// If obj is provided, the promise aspect is added to the object
						promise: function (obj) {
							return obj != null ? jQuery.extend(obj, promise) : promise;
						}
					},
					deferred = {};

				// Add list-specific methods
				jQuery.each(tuples, function (i, tuple) {
					var list = tuple[2],
						stateString = tuple[5];

					// promise.progress = list.add
					// promise.done = list.add
					// promise.fail = list.add
					promise[tuple[1]] = list.add;

					// Handle state
					if (stateString) {
						list.add(
							function () {

								// state = "resolved" (i.e., fulfilled)
								// state = "rejected"
								state = stateString;
							},

							// rejected_callbacks.disable
							// fulfilled_callbacks.disable
							tuples[3 - i][2].disable,

							// progress_callbacks.lock
							tuples[0][2].lock
						);
					}

					// progress_handlers.fire
					// fulfilled_handlers.fire
					// rejected_handlers.fire
					list.add(tuple[3].fire);

					// deferred.notify = function() { deferred.notifyWith(...) }
					// deferred.resolve = function() { deferred.resolveWith(...) }
					// deferred.reject = function() { deferred.rejectWith(...) }
					deferred[tuple[0]] = function () {
						deferred[tuple[0] + "With"](this === deferred ? undefined : this, arguments);
						return this;
					};

					// deferred.notifyWith = list.fireWith
					// deferred.resolveWith = list.fireWith
					// deferred.rejectWith = list.fireWith
					deferred[tuple[0] + "With"] = list.fireWith;
				});

				// Make the deferred a promise
				promise.promise(deferred);

				// Call given func if any
				if (func) {
					func.call(deferred, deferred);
				}

				// All done!
				return deferred;
			},

			// Deferred helper
			when: function (singleValue) {
				var

					// count of uncompleted subordinates
					remaining = arguments.length,

					// count of unprocessed arguments
					i = remaining,

					// subordinate fulfillment data
					resolveContexts = Array(i),
					resolveValues = slice.call(arguments),

					// the master Deferred
					master = jQuery.Deferred(),

					// subordinate callback factory
					updateFunc = function (i) {
						return function (value) {
							resolveContexts[i] = this;
							resolveValues[i] = arguments.length > 1 ? slice.call(arguments) : value;
							if (!(--remaining)) {
								master.resolveWith(resolveContexts, resolveValues);
							}
						};
					};

				// Single- and empty arguments are adopted like Promise.resolve
				if (remaining <= 1) {
					adoptValue(singleValue, master.done(updateFunc(i)).resolve, master.reject,
						!remaining);

					// Use .then() to unwrap secondary thenables (cf. gh-3000)
					if (master.state() === "pending" ||
						jQuery.isFunction(resolveValues[i] && resolveValues[i].then)) {

						return master.then();
					}
				}

				// Multiple arguments are aggregated like Promise.all array elements
				while (i--) {
					adoptValue(resolveValues[i], updateFunc(i), master.reject);
				}

				return master.promise();
			}
		});


		// These usually indicate a programmer mistake during development,
		// warn about them ASAP rather than swallowing them by default.
		var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;

		jQuery.Deferred.exceptionHook = function (error, stack) {

			// Support: IE 8 - 9 only
			// Console exists when dev tools are open, which can happen at any time
			if (window.console && window.console.warn && error && rerrorNames.test(error.name)) {
				window.console.warn("jQuery.Deferred exception: " + error.message, error.stack, stack);
			}
		};




		jQuery.readyException = function (error) {
			window.setTimeout(function () {
				throw error;
			});
		};




		// The deferred used on DOM ready
		var readyList = jQuery.Deferred();

		jQuery.fn.ready = function (fn) {

			readyList
				.then(fn)

				// Wrap jQuery.readyException in a function so that the lookup
				// happens at the time of error handling instead of callback
				// registration.
				.catch(function (error) {
					jQuery.readyException(error);
				});

			return this;
		};

		jQuery.extend({

			// Is the DOM ready to be used? Set to true once it occurs.
			isReady: false,

			// A counter to track how many items to wait for before
			// the ready event fires. See #6781
			readyWait: 1,

			// Handle when the DOM is ready
			ready: function (wait) {

				// Abort if there are pending holds or we're already ready
				if (wait === true ? --jQuery.readyWait : jQuery.isReady) {
					return;
				}

				// Remember that the DOM is ready
				jQuery.isReady = true;

				// If a normal DOM Ready event fired, decrement, and wait if need be
				if (wait !== true && --jQuery.readyWait > 0) {
					return;
				}

				// If there are functions bound, to execute
				readyList.resolveWith(document, [jQuery]);
			}
		});

		jQuery.ready.then = readyList.then;

		// The ready event handler and self cleanup method
		function completed() {
			document.removeEventListener("DOMContentLoaded", completed);
			window.removeEventListener("load", completed);
			jQuery.ready();
		}

		// Catch cases where $(document).ready() is called
		// after the browser event has already occurred.
		// Support: IE <=9 - 10 only
		// Older IE sometimes signals "interactive" too soon
		if (document.readyState === "complete" ||
			(document.readyState !== "loading" && !document.documentElement.doScroll)) {

			// Handle it asynchronously to allow scripts the opportunity to delay ready
			window.setTimeout(jQuery.ready);

		} else {

			// Use the handy event callback
			document.addEventListener("DOMContentLoaded", completed);

			// A fallback to window.onload, that will always work
			window.addEventListener("load", completed);
		}




		// Multifunctional method to get and set values of a collection
		// The value/s can optionally be executed if it's a function
		var access = function (elems, fn, key, value, chainable, emptyGet, raw) {
			var i = 0,
				len = elems.length,
				bulk = key == null;

			// Sets many values
			if (jQuery.type(key) === "object") {
				chainable = true;
				for (i in key) {
					access(elems, fn, i, key[i], true, emptyGet, raw);
				}

				// Sets one value
			} else if (value !== undefined) {
				chainable = true;

				if (!jQuery.isFunction(value)) {
					raw = true;
				}

				if (bulk) {

					// Bulk operations run against the entire set
					if (raw) {
						fn.call(elems, value);
						fn = null;

						// ...except when executing function values
					} else {
						bulk = fn;
						fn = function (elem, key, value) {
							return bulk.call(jQuery(elem), value);
						};
					}
				}

				if (fn) {
					for (; i < len; i++) {
						fn(
							elems[i], key, raw ?
							value :
							value.call(elems[i], i, fn(elems[i], key))
						);
					}
				}
			}

			if (chainable) {
				return elems;
			}

			// Gets
			if (bulk) {
				return fn.call(elems);
			}

			return len ? fn(elems[0], key) : emptyGet;
		};
		var acceptData = function (owner) {

			// Accepts only:
			//  - Node
			//    - Node.ELEMENT_NODE
			//    - Node.DOCUMENT_NODE
			//  - Object
			//    - Any
			return owner.nodeType === 1 || owner.nodeType === 9 || !(+owner.nodeType);
		};




		function Data() {
			this.expando = jQuery.expando + Data.uid++;
		}

		Data.uid = 1;

		Data.prototype = {

			cache: function (owner) {

				// Check if the owner object already has a cache
				var value = owner[this.expando];

				// If not, create one
				if (!value) {
					value = {};

					// We can accept data for non-element nodes in modern browsers,
					// but we should not, see #8335.
					// Always return an empty object.
					if (acceptData(owner)) {

						// If it is a node unlikely to be stringify-ed or looped over
						// use plain assignment
						if (owner.nodeType) {
							owner[this.expando] = value;

							// Otherwise secure it in a non-enumerable property
							// configurable must be true to allow the property to be
							// deleted when data is removed
						} else {
							Object.defineProperty(owner, this.expando, {
								value: value,
								configurable: true
							});
						}
					}
				}

				return value;
			},
			set: function (owner, data, value) {
				var prop,
					cache = this.cache(owner);

				// Handle: [ owner, key, value ] args
				// Always use camelCase key (gh-2257)
				if (typeof data === "string") {
					cache[jQuery.camelCase(data)] = value;

					// Handle: [ owner, { properties } ] args
				} else {

					// Copy the properties one-by-one to the cache object
					for (prop in data) {
						cache[jQuery.camelCase(prop)] = data[prop];
					}
				}
				return cache;
			},
			get: function (owner, key) {
				return key === undefined ?
					this.cache(owner) :

					// Always use camelCase key (gh-2257)
					owner[this.expando] && owner[this.expando][jQuery.camelCase(key)];
			},
			access: function (owner, key, value) {

				// In cases where either:
				//
				//   1. No key was specified
				//   2. A string key was specified, but no value provided
				//
				// Take the "read" path and allow the get method to determine
				// which value to return, respectively either:
				//
				//   1. The entire cache object
				//   2. The data stored at the key
				//
				if (key === undefined ||
					((key && typeof key === "string") && value === undefined)) {

					return this.get(owner, key);
				}

				// When the key is not a string, or both a key and value
				// are specified, set or extend (existing objects) with either:
				//
				//   1. An object of properties
				//   2. A key and value
				//
				this.set(owner, key, value);

				// Since the "set" path can have two possible entry points
				// return the expected data based on which path was taken[*]
				return value !== undefined ? value : key;
			},
			remove: function (owner, key) {
				var i,
					cache = owner[this.expando];

				if (cache === undefined) {
					return;
				}

				if (key !== undefined) {

					// Support array or space separated string of keys
					if (Array.isArray(key)) {

						// If key is an array of keys...
						// We always set camelCase keys, so remove that.
						key = key.map(jQuery.camelCase);
					} else {
						key = jQuery.camelCase(key);

						// If a key with the spaces exists, use it.
						// Otherwise, create an array by matching non-whitespace
						key = key in cache ?
							[key] :
							(key.match(rnothtmlwhite) || []);
					}

					i = key.length;

					while (i--) {
						delete cache[key[i]];
					}
				}

				// Remove the expando if there's no more data
				if (key === undefined || jQuery.isEmptyObject(cache)) {

					// Support: Chrome <=35 - 45
					// Webkit & Blink performance suffers when deleting properties
					// from DOM nodes, so set to undefined instead
					// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
					if (owner.nodeType) {
						owner[this.expando] = undefined;
					} else {
						delete owner[this.expando];
					}
				}
			},
			hasData: function (owner) {
				var cache = owner[this.expando];
				return cache !== undefined && !jQuery.isEmptyObject(cache);
			}
		};
		var dataPriv = new Data();

		var dataUser = new Data();



		//	Implementation Summary
		//
		//	1. Enforce API surface and semantic compatibility with 1.9.x branch
		//	2. Improve the module's maintainability by reducing the storage
		//		paths to a single mechanism.
		//	3. Use the same single mechanism to support "private" and "user" data.
		//	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
		//	5. Avoid exposing implementation details on user objects (eg. expando properties)
		//	6. Provide a clear path for implementation upgrade to WeakMap in 2014

		var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
			rmultiDash = /[A-Z]/g;

		function getData(data) {
			if (data === "true") {
				return true;
			}

			if (data === "false") {
				return false;
			}

			if (data === "null") {
				return null;
			}

			// Only convert to a number if it doesn't change the string
			if (data === +data + "") {
				return +data;
			}

			if (rbrace.test(data)) {
				return JSON.parse(data);
			}

			return data;
		}

		function dataAttr(elem, key, data) {
			var name;

			// If nothing was found internally, try to fetch any
			// data from the HTML5 data-* attribute
			if (data === undefined && elem.nodeType === 1) {
				name = "data-" + key.replace(rmultiDash, "-$&").toLowerCase();
				data = elem.getAttribute(name);

				if (typeof data === "string") {
					try {
						data = getData(data);
					} catch (e) { }

					// Make sure we set the data so it isn't changed later
					dataUser.set(elem, key, data);
				} else {
					data = undefined;
				}
			}
			return data;
		}

		jQuery.extend({
			hasData: function (elem) {
				return dataUser.hasData(elem) || dataPriv.hasData(elem);
			},

			data: function (elem, name, data) {
				return dataUser.access(elem, name, data);
			},

			removeData: function (elem, name) {
				dataUser.remove(elem, name);
			},

			// TODO: Now that all calls to _data and _removeData have been replaced
			// with direct calls to dataPriv methods, these can be deprecated.
			_data: function (elem, name, data) {
				return dataPriv.access(elem, name, data);
			},

			_removeData: function (elem, name) {
				dataPriv.remove(elem, name);
			}
		});

		jQuery.fn.extend({
			data: function (key, value) {
				var i, name, data,
					elem = this[0],
					attrs = elem && elem.attributes;

				// Gets all values
				if (key === undefined) {
					if (this.length) {
						data = dataUser.get(elem);

						if (elem.nodeType === 1 && !dataPriv.get(elem, "hasDataAttrs")) {
							i = attrs.length;
							while (i--) {

								// Support: IE 11 only
								// The attrs elements can be null (#14894)
								if (attrs[i]) {
									name = attrs[i].name;
									if (name.indexOf("data-") === 0) {
										name = jQuery.camelCase(name.slice(5));
										dataAttr(elem, name, data[name]);
									}
								}
							}
							dataPriv.set(elem, "hasDataAttrs", true);
						}
					}

					return data;
				}

				// Sets multiple values
				if (typeof key === "object") {
					return this.each(function () {
						dataUser.set(this, key);
					});
				}

				return access(this, function (value) {
					var data;

					// The calling jQuery object (element matches) is not empty
					// (and therefore has an element appears at this[ 0 ]) and the
					// `value` parameter was not undefined. An empty jQuery object
					// will result in `undefined` for elem = this[ 0 ] which will
					// throw an exception if an attempt to read a data cache is made.
					if (elem && value === undefined) {

						// Attempt to get data from the cache
						// The key will always be camelCased in Data
						data = dataUser.get(elem, key);
						if (data !== undefined) {
							return data;
						}

						// Attempt to "discover" the data in
						// HTML5 custom data-* attrs
						data = dataAttr(elem, key);
						if (data !== undefined) {
							return data;
						}

						// We tried really hard, but the data doesn't exist.
						return;
					}

					// Set the data...
					this.each(function () {

						// We always store the camelCased key
						dataUser.set(this, key, value);
					});
				}, null, value, arguments.length > 1, null, true);
			},

			removeData: function (key) {
				return this.each(function () {
					dataUser.remove(this, key);
				});
			}
		});


		jQuery.extend({
			queue: function (elem, type, data) {
				var queue;

				if (elem) {
					type = (type || "fx") + "queue";
					queue = dataPriv.get(elem, type);

					// Speed up dequeue by getting out quickly if this is just a lookup
					if (data) {
						if (!queue || Array.isArray(data)) {
							queue = dataPriv.access(elem, type, jQuery.makeArray(data));
						} else {
							queue.push(data);
						}
					}
					return queue || [];
				}
			},

			dequeue: function (elem, type) {
				type = type || "fx";

				var queue = jQuery.queue(elem, type),
					startLength = queue.length,
					fn = queue.shift(),
					hooks = jQuery._queueHooks(elem, type),
					next = function () {
						jQuery.dequeue(elem, type);
					};

				// If the fx queue is dequeued, always remove the progress sentinel
				if (fn === "inprogress") {
					fn = queue.shift();
					startLength--;
				}

				if (fn) {

					// Add a progress sentinel to prevent the fx queue from being
					// automatically dequeued
					if (type === "fx") {
						queue.unshift("inprogress");
					}

					// Clear up the last queue stop function
					delete hooks.stop;
					fn.call(elem, next, hooks);
				}

				if (!startLength && hooks) {
					hooks.empty.fire();
				}
			},

			// Not public - generate a queueHooks object, or return the current one
			_queueHooks: function (elem, type) {
				var key = type + "queueHooks";
				return dataPriv.get(elem, key) || dataPriv.access(elem, key, {
					empty: jQuery.Callbacks("once memory").add(function () {
						dataPriv.remove(elem, [type + "queue", key]);
					})
				});
			}
		});

		jQuery.fn.extend({
			queue: function (type, data) {
				var setter = 2;

				if (typeof type !== "string") {
					data = type;
					type = "fx";
					setter--;
				}

				if (arguments.length < setter) {
					return jQuery.queue(this[0], type);
				}

				return data === undefined ?
					this :
					this.each(function () {
						var queue = jQuery.queue(this, type, data);

						// Ensure a hooks for this queue
						jQuery._queueHooks(this, type);

						if (type === "fx" && queue[0] !== "inprogress") {
							jQuery.dequeue(this, type);
						}
					});
			},
			dequeue: function (type) {
				return this.each(function () {
					jQuery.dequeue(this, type);
				});
			},
			clearQueue: function (type) {
				return this.queue(type || "fx", []);
			},

			// Get a promise resolved when queues of a certain type
			// are emptied (fx is the type by default)
			promise: function (type, obj) {
				var tmp,
					count = 1,
					defer = jQuery.Deferred(),
					elements = this,
					i = this.length,
					resolve = function () {
						if (!(--count)) {
							defer.resolveWith(elements, [elements]);
						}
					};

				if (typeof type !== "string") {
					obj = type;
					type = undefined;
				}
				type = type || "fx";

				while (i--) {
					tmp = dataPriv.get(elements[i], type + "queueHooks");
					if (tmp && tmp.empty) {
						count++;
						tmp.empty.add(resolve);
					}
				}
				resolve();
				return defer.promise(obj);
			}
		});
		var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;

		var rcssNum = new RegExp("^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i");


		var cssExpand = ["Top", "Right", "Bottom", "Left"];

		var isHiddenWithinTree = function (elem, el) {

			// isHiddenWithinTree might be called from jQuery#filter function;
			// in that case, element will be second argument
			elem = el || elem;

			// Inline style trumps all
			return elem.style.display === "none" ||
				elem.style.display === "" &&

				// Otherwise, check computed style
				// Support: Firefox <=43 - 45
				// Disconnected elements can have computed display: none, so first confirm that elem is
				// in the document.
				jQuery.contains(elem.ownerDocument, elem) &&

				jQuery.css(elem, "display") === "none";
		};

		var swap = function (elem, options, callback, args) {
			var ret, name,
				old = {};

			// Remember the old values, and insert the new ones
			for (name in options) {
				old[name] = elem.style[name];
				elem.style[name] = options[name];
			}

			ret = callback.apply(elem, args || []);

			// Revert the old values
			for (name in options) {
				elem.style[name] = old[name];
			}

			return ret;
		};




		function adjustCSS(elem, prop, valueParts, tween) {
			var adjusted,
				scale = 1,
				maxIterations = 20,
				currentValue = tween ?
					function () {
						return tween.cur();
					} :
					function () {
						return jQuery.css(elem, prop, "");
					},
				initial = currentValue(),
				unit = valueParts && valueParts[3] || (jQuery.cssNumber[prop] ? "" : "px"),

				// Starting value computation is required for potential unit mismatches
				initialInUnit = (jQuery.cssNumber[prop] || unit !== "px" && +initial) &&
					rcssNum.exec(jQuery.css(elem, prop));

			if (initialInUnit && initialInUnit[3] !== unit) {

				// Trust units reported by jQuery.css
				unit = unit || initialInUnit[3];

				// Make sure we update the tween properties later on
				valueParts = valueParts || [];

				// Iteratively approximate from a nonzero starting point
				initialInUnit = +initial || 1;

				do {

					// If previous iteration zeroed out, double until we get *something*.
					// Use string for doubling so we don't accidentally see scale as unchanged below
					scale = scale || ".5";

					// Adjust and apply
					initialInUnit = initialInUnit / scale;
					jQuery.style(elem, prop, initialInUnit + unit);

					// Update scale, tolerating zero or NaN from tween.cur()
					// Break the loop if scale is unchanged or perfect, or if we've just had enough.
				} while (
					scale !== (scale = currentValue() / initial) && scale !== 1 && --maxIterations
				);
			}

			if (valueParts) {
				initialInUnit = +initialInUnit || +initial || 0;

				// Apply relative offset (+=/-=) if specified
				adjusted = valueParts[1] ?
					initialInUnit + (valueParts[1] + 1) * valueParts[2] :
					+valueParts[2];
				if (tween) {
					tween.unit = unit;
					tween.start = initialInUnit;
					tween.end = adjusted;
				}
			}
			return adjusted;
		}


		var defaultDisplayMap = {};

		function getDefaultDisplay(elem) {
			var temp,
				doc = elem.ownerDocument,
				nodeName = elem.nodeName,
				display = defaultDisplayMap[nodeName];

			if (display) {
				return display;
			}

			temp = doc.body.appendChild(doc.createElement(nodeName));
			display = jQuery.css(temp, "display");

			temp.parentNode.removeChild(temp);

			if (display === "none") {
				display = "block";
			}
			defaultDisplayMap[nodeName] = display;

			return display;
		}

		function showHide(elements, show) {
			var display, elem,
				values = [],
				index = 0,
				length = elements.length;

			// Determine new display value for elements that need to change
			for (; index < length; index++) {
				elem = elements[index];
				if (!elem.style) {
					continue;
				}

				display = elem.style.display;
				if (show) {

					// Since we force visibility upon cascade-hidden elements, an immediate (and slow)
					// check is required in this first loop unless we have a nonempty display value (either
					// inline or about-to-be-restored)
					if (display === "none") {
						values[index] = dataPriv.get(elem, "display") || null;
						if (!values[index]) {
							elem.style.display = "";
						}
					}
					if (elem.style.display === "" && isHiddenWithinTree(elem)) {
						values[index] = getDefaultDisplay(elem);
					}
				} else {
					if (display !== "none") {
						values[index] = "none";

						// Remember what we're overwriting
						dataPriv.set(elem, "display", display);
					}
				}
			}

			// Set the display of the elements in a second loop to avoid constant reflow
			for (index = 0; index < length; index++) {
				if (values[index] != null) {
					elements[index].style.display = values[index];
				}
			}

			return elements;
		}

		jQuery.fn.extend({
			show: function () {
				return showHide(this, true);
			},
			hide: function () {
				return showHide(this);
			},
			toggle: function (state) {
				if (typeof state === "boolean") {
					return state ? this.show() : this.hide();
				}

				return this.each(function () {
					if (isHiddenWithinTree(this)) {
						jQuery(this).show();
					} else {
						jQuery(this).hide();
					}
				});
			}
		});
		var rcheckableType = (/^(?:checkbox|radio)$/i);

		var rtagName = (/<([a-z][^\/\0>\x20\t\r\n\f]+)/i);

		var rscriptType = (/^$|\/(?:java|ecma)script/i);



		// We have to close these tags to support XHTML (#13200)
		var wrapMap = {

			// Support: IE <=9 only
			option: [1, "<select multiple='multiple'>", "</select>"],

			// XHTML parsers do not magically insert elements in the
			// same way that tag soup parsers do. So we cannot shorten
			// this by omitting <tbody> or other required elements.
			thead: [1, "<table>", "</table>"],
			col: [2, "<table><colgroup>", "</colgroup></table>"],
			tr: [2, "<table><tbody>", "</tbody></table>"],
			td: [3, "<table><tbody><tr>", "</tr></tbody></table>"],

			_default: [0, "", ""]
		};

		// Support: IE <=9 only
		wrapMap.optgroup = wrapMap.option;

		wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
		wrapMap.th = wrapMap.td;


		function getAll(context, tag) {

			// Support: IE <=9 - 11 only
			// Use typeof to avoid zero-argument method invocation on host objects (#15151)
			var ret;

			if (typeof context.getElementsByTagName !== "undefined") {
				ret = context.getElementsByTagName(tag || "*");

			} else if (typeof context.querySelectorAll !== "undefined") {
				ret = context.querySelectorAll(tag || "*");

			} else {
				ret = [];
			}

			if (tag === undefined || tag && nodeName(context, tag)) {
				return jQuery.merge([context], ret);
			}

			return ret;
		}


		// Mark scripts as having already been evaluated
		function setGlobalEval(elems, refElements) {
			var i = 0,
				l = elems.length;

			for (; i < l; i++) {
				dataPriv.set(
					elems[i],
					"globalEval",
					!refElements || dataPriv.get(refElements[i], "globalEval")
				);
			}
		}


		var rhtml = /<|&#?\w+;/;

		function buildFragment(elems, context, scripts, selection, ignored) {
			var elem, tmp, tag, wrap, contains, j,
				fragment = context.createDocumentFragment(),
				nodes = [],
				i = 0,
				l = elems.length;

			for (; i < l; i++) {
				elem = elems[i];

				if (elem || elem === 0) {

					// Add nodes directly
					if (jQuery.type(elem) === "object") {

						// Support: Android <=4.0 only, PhantomJS 1 only
						// push.apply(_, arraylike) throws on ancient WebKit
						jQuery.merge(nodes, elem.nodeType ? [elem] : elem);

						// Convert non-html into a text node
					} else if (!rhtml.test(elem)) {
						nodes.push(context.createTextNode(elem));

						// Convert html into DOM nodes
					} else {
						tmp = tmp || fragment.appendChild(context.createElement("div"));

						// Deserialize a standard representation
						tag = (rtagName.exec(elem) || ["", ""])[1].toLowerCase();
						wrap = wrapMap[tag] || wrapMap._default;
						tmp.innerHTML = wrap[1] + jQuery.htmlPrefilter(elem) + wrap[2];

						// Descend through wrappers to the right content
						j = wrap[0];
						while (j--) {
							tmp = tmp.lastChild;
						}

						// Support: Android <=4.0 only, PhantomJS 1 only
						// push.apply(_, arraylike) throws on ancient WebKit
						jQuery.merge(nodes, tmp.childNodes);

						// Remember the top-level container
						tmp = fragment.firstChild;

						// Ensure the created nodes are orphaned (#12392)
						tmp.textContent = "";
					}
				}
			}

			// Remove wrapper from fragment
			fragment.textContent = "";

			i = 0;
			while ((elem = nodes[i++])) {

				// Skip elements already in the context collection (trac-4087)
				if (selection && jQuery.inArray(elem, selection) > -1) {
					if (ignored) {
						ignored.push(elem);
					}
					continue;
				}

				contains = jQuery.contains(elem.ownerDocument, elem);

				// Append to fragment
				tmp = getAll(fragment.appendChild(elem), "script");

				// Preserve script evaluation history
				if (contains) {
					setGlobalEval(tmp);
				}

				// Capture executables
				if (scripts) {
					j = 0;
					while ((elem = tmp[j++])) {
						if (rscriptType.test(elem.type || "")) {
							scripts.push(elem);
						}
					}
				}
			}

			return fragment;
		}


		(function () {
			var fragment = document.createDocumentFragment(),
				div = fragment.appendChild(document.createElement("div")),
				input = document.createElement("input");

			// Support: Android 4.0 - 4.3 only
			// Check state lost if the name is set (#11217)
			// Support: Windows Web Apps (WWA)
			// `name` and `type` must use .setAttribute for WWA (#14901)
			input.setAttribute("type", "radio");
			input.setAttribute("checked", "checked");
			input.setAttribute("name", "t");

			div.appendChild(input);

			// Support: Android <=4.1 only
			// Older WebKit doesn't clone checked state correctly in fragments
			support.checkClone = div.cloneNode(true).cloneNode(true).lastChild.checked;

			// Support: IE <=11 only
			// Make sure textarea (and checkbox) defaultValue is properly cloned
			div.innerHTML = "<textarea>x</textarea>";
			support.noCloneChecked = !!div.cloneNode(true).lastChild.defaultValue;
		})();
		var documentElement = document.documentElement;



		var
			rkeyEvent = /^key/,
			rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
			rtypenamespace = /^([^.]*)(?:\.(.+)|)/;

		function returnTrue() {
			return true;
		}

		function returnFalse() {
			return false;
		}

		// Support: IE <=9 only
		// See #13393 for more info
		function safeActiveElement() {
			try {
				return document.activeElement;
			} catch (err) { }
		}

		function on(elem, types, selector, data, fn, one) {
			var origFn, type;

			// Types can be a map of types/handlers
			if (typeof types === "object") {

				// ( types-Object, selector, data )
				if (typeof selector !== "string") {

					// ( types-Object, data )
					data = data || selector;
					selector = undefined;
				}
				for (type in types) {
					on(elem, type, selector, data, types[type], one);
				}
				return elem;
			}

			if (data == null && fn == null) {

				// ( types, fn )
				fn = selector;
				data = selector = undefined;
			} else if (fn == null) {
				if (typeof selector === "string") {

					// ( types, selector, fn )
					fn = data;
					data = undefined;
				} else {

					// ( types, data, fn )
					fn = data;
					data = selector;
					selector = undefined;
				}
			}
			if (fn === false) {
				fn = returnFalse;
			} else if (!fn) {
				return elem;
			}

			if (one === 1) {
				origFn = fn;
				fn = function (event) {

					// Can use an empty set, since event contains the info
					jQuery().off(event);
					return origFn.apply(this, arguments);
				};

				// Use same guid so caller can remove using origFn
				fn.guid = origFn.guid || (origFn.guid = jQuery.guid++);
			}
			return elem.each(function () {
				jQuery.event.add(this, types, fn, data, selector);
			});
		}

		/*
		 * Helper functions for managing events -- not part of the public interface.
		 * Props to Dean Edwards' addEvent library for many of the ideas.
		 */
		jQuery.event = {

			global: {},

			add: function (elem, types, handler, data, selector) {

				var handleObjIn, eventHandle, tmp,
					events, t, handleObj,
					special, handlers, type, namespaces, origType,
					elemData = dataPriv.get(elem);

				// Don't attach events to noData or text/comment nodes (but allow plain objects)
				if (!elemData) {
					return;
				}

				// Caller can pass in an object of custom data in lieu of the handler
				if (handler.handler) {
					handleObjIn = handler;
					handler = handleObjIn.handler;
					selector = handleObjIn.selector;
				}

				// Ensure that invalid selectors throw exceptions at attach time
				// Evaluate against documentElement in case elem is a non-element node (e.g., document)
				if (selector) {
					jQuery.find.matchesSelector(documentElement, selector);
				}

				// Make sure that the handler has a unique ID, used to find/remove it later
				if (!handler.guid) {
					handler.guid = jQuery.guid++;
				}

				// Init the element's event structure and main handler, if this is the first
				if (!(events = elemData.events)) {
					events = elemData.events = {};
				}
				if (!(eventHandle = elemData.handle)) {
					eventHandle = elemData.handle = function (e) {

						// Discard the second event of a jQuery.event.trigger() and
						// when an event is called after a page has unloaded
						return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
							jQuery.event.dispatch.apply(elem, arguments) : undefined;
					};
				}

				// Handle multiple events separated by a space
				types = (types || "").match(rnothtmlwhite) || [""];
				t = types.length;
				while (t--) {
					tmp = rtypenamespace.exec(types[t]) || [];
					type = origType = tmp[1];
					namespaces = (tmp[2] || "").split(".").sort();

					// There *must* be a type, no attaching namespace-only handlers
					if (!type) {
						continue;
					}

					// If event changes its type, use the special event handlers for the changed type
					special = jQuery.event.special[type] || {};

					// If selector defined, determine special event api type, otherwise given type
					type = (selector ? special.delegateType : special.bindType) || type;

					// Update special based on newly reset type
					special = jQuery.event.special[type] || {};

					// handleObj is passed to all event handlers
					handleObj = jQuery.extend({
						type: type,
						origType: origType,
						data: data,
						handler: handler,
						guid: handler.guid,
						selector: selector,
						needsContext: selector && jQuery.expr.match.needsContext.test(selector),
						namespace: namespaces.join(".")
					}, handleObjIn);

					// Init the event handler queue if we're the first
					if (!(handlers = events[type])) {
						handlers = events[type] = [];
						handlers.delegateCount = 0;

						// Only use addEventListener if the special events handler returns false
						if (!special.setup ||
							special.setup.call(elem, data, namespaces, eventHandle) === false) {

							if (elem.addEventListener) {
								elem.addEventListener(type, eventHandle);
							}
						}
					}

					if (special.add) {
						special.add.call(elem, handleObj);

						if (!handleObj.handler.guid) {
							handleObj.handler.guid = handler.guid;
						}
					}

					// Add to the element's handler list, delegates in front
					if (selector) {
						handlers.splice(handlers.delegateCount++, 0, handleObj);
					} else {
						handlers.push(handleObj);
					}

					// Keep track of which events have ever been used, for event optimization
					jQuery.event.global[type] = true;
				}

			},

			// Detach an event or set of events from an element
			remove: function (elem, types, handler, selector, mappedTypes) {

				var j, origCount, tmp,
					events, t, handleObj,
					special, handlers, type, namespaces, origType,
					elemData = dataPriv.hasData(elem) && dataPriv.get(elem);

				if (!elemData || !(events = elemData.events)) {
					return;
				}

				// Once for each type.namespace in types; type may be omitted
				types = (types || "").match(rnothtmlwhite) || [""];
				t = types.length;
				while (t--) {
					tmp = rtypenamespace.exec(types[t]) || [];
					type = origType = tmp[1];
					namespaces = (tmp[2] || "").split(".").sort();

					// Unbind all events (on this namespace, if provided) for the element
					if (!type) {
						for (type in events) {
							jQuery.event.remove(elem, type + types[t], handler, selector, true);
						}
						continue;
					}

					special = jQuery.event.special[type] || {};
					type = (selector ? special.delegateType : special.bindType) || type;
					handlers = events[type] || [];
					tmp = tmp[2] &&
						new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)");

					// Remove matching events
					origCount = j = handlers.length;
					while (j--) {
						handleObj = handlers[j];

						if ((mappedTypes || origType === handleObj.origType) &&
							(!handler || handler.guid === handleObj.guid) &&
							(!tmp || tmp.test(handleObj.namespace)) &&
							(!selector || selector === handleObj.selector ||
								selector === "**" && handleObj.selector)) {
							handlers.splice(j, 1);

							if (handleObj.selector) {
								handlers.delegateCount--;
							}
							if (special.remove) {
								special.remove.call(elem, handleObj);
							}
						}
					}

					// Remove generic event handler if we removed something and no more handlers exist
					// (avoids potential for endless recursion during removal of special event handlers)
					if (origCount && !handlers.length) {
						if (!special.teardown ||
							special.teardown.call(elem, namespaces, elemData.handle) === false) {

							jQuery.removeEvent(elem, type, elemData.handle);
						}

						delete events[type];
					}
				}

				// Remove data and the expando if it's no longer used
				if (jQuery.isEmptyObject(events)) {
					dataPriv.remove(elem, "handle events");
				}
			},

			dispatch: function (nativeEvent) {
				var arguments$1 = arguments;


				// Make a writable jQuery.Event from the native event object
				var event = jQuery.event.fix(nativeEvent);

				var i, j, ret, matched, handleObj, handlerQueue,
					args = new Array(arguments.length),
					handlers = (dataPriv.get(this, "events") || {})[event.type] || [],
					special = jQuery.event.special[event.type] || {};

				// Use the fix-ed jQuery.Event rather than the (read-only) native event
				args[0] = event;

				for (i = 1; i < arguments.length; i++) {
					args[i] = arguments$1[i];
				}

				event.delegateTarget = this;

				// Call the preDispatch hook for the mapped type, and let it bail if desired
				if (special.preDispatch && special.preDispatch.call(this, event) === false) {
					return;
				}

				// Determine handlers
				handlerQueue = jQuery.event.handlers.call(this, event, handlers);

				// Run delegates first; they may want to stop propagation beneath us
				i = 0;
				while ((matched = handlerQueue[i++]) && !event.isPropagationStopped()) {
					event.currentTarget = matched.elem;

					j = 0;
					while ((handleObj = matched.handlers[j++]) &&
						!event.isImmediatePropagationStopped()) {

						// Triggered event must either 1) have no namespace, or 2) have namespace(s)
						// a subset or equal to those in the bound event (both can have no namespace).
						if (!event.rnamespace || event.rnamespace.test(handleObj.namespace)) {

							event.handleObj = handleObj;
							event.data = handleObj.data;

							ret = ((jQuery.event.special[handleObj.origType] || {}).handle ||
								handleObj.handler).apply(matched.elem, args);

							if (ret !== undefined) {
								if ((event.result = ret) === false) {
									event.preventDefault();
									event.stopPropagation();
								}
							}
						}
					}
				}

				// Call the postDispatch hook for the mapped type
				if (special.postDispatch) {
					special.postDispatch.call(this, event);
				}

				return event.result;
			},

			handlers: function (event, handlers) {
				var this$1 = this;

				var i, handleObj, sel, matchedHandlers, matchedSelectors,
					handlerQueue = [],
					delegateCount = handlers.delegateCount,
					cur = event.target;

				// Find delegate handlers
				if (delegateCount &&

					// Support: IE <=9
					// Black-hole SVG <use> instance trees (trac-13180)
					cur.nodeType &&

					// Support: Firefox <=42
					// Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
					// https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
					// Support: IE 11 only
					// ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
					!(event.type === "click" && event.button >= 1)) {

					for (; cur !== this; cur = cur.parentNode || this) {

						// Don't check non-elements (#13208)
						// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
						if (cur.nodeType === 1 && !(event.type === "click" && cur.disabled === true)) {
							matchedHandlers = [];
							matchedSelectors = {};
							for (i = 0; i < delegateCount; i++) {
								handleObj = handlers[i];

								// Don't conflict with Object.prototype properties (#13203)
								sel = handleObj.selector + " ";

								if (matchedSelectors[sel] === undefined) {
									matchedSelectors[sel] = handleObj.needsContext ?
										jQuery(sel, this$1).index(cur) > -1 :
										jQuery.find(sel, this$1, null, [cur]).length;
								}
								if (matchedSelectors[sel]) {
									matchedHandlers.push(handleObj);
								}
							}
							if (matchedHandlers.length) {
								handlerQueue.push({ elem: cur, handlers: matchedHandlers });
							}
						}
					}
				}

				// Add the remaining (directly-bound) handlers
				cur = this;
				if (delegateCount < handlers.length) {
					handlerQueue.push({ elem: cur, handlers: handlers.slice(delegateCount) });
				}

				return handlerQueue;
			},

			addProp: function (name, hook) {
				Object.defineProperty(jQuery.Event.prototype, name, {
					enumerable: true,
					configurable: true,

					get: jQuery.isFunction(hook) ?
						function () {
							if (this.originalEvent) {
								return hook(this.originalEvent);
							}
						} :
						function () {
							if (this.originalEvent) {
								return this.originalEvent[name];
							}
						},

					set: function (value) {
						Object.defineProperty(this, name, {
							enumerable: true,
							configurable: true,
							writable: true,
							value: value
						});
					}
				});
			},

			fix: function (originalEvent) {
				return originalEvent[jQuery.expando] ?
					originalEvent :
					new jQuery.Event(originalEvent);
			},

			special: {
				load: {

					// Prevent triggered image.load events from bubbling to window.load
					noBubble: true
				},
				focus: {

					// Fire native event if possible so blur/focus sequence is correct
					trigger: function () {
						if (this !== safeActiveElement() && this.focus) {
							this.focus();
							return false;
						}
					},
					delegateType: "focusin"
				},
				blur: {
					trigger: function () {
						if (this === safeActiveElement() && this.blur) {
							this.blur();
							return false;
						}
					},
					delegateType: "focusout"
				},
				click: {

					// For checkbox, fire native event so checked state will be right
					trigger: function () {
						if (this.type === "checkbox" && this.click && nodeName(this, "input")) {
							this.click();
							return false;
						}
					},

					// For cross-browser consistency, don't fire native .click() on links
					_default: function (event) {
						return nodeName(event.target, "a");
					}
				},

				beforeunload: {
					postDispatch: function (event) {

						// Support: Firefox 20+
						// Firefox doesn't alert if the returnValue field is not set.
						if (event.result !== undefined && event.originalEvent) {
							event.originalEvent.returnValue = event.result;
						}
					}
				}
			}
		};

		jQuery.removeEvent = function (elem, type, handle) {

			// This "if" is needed for plain objects
			if (elem.removeEventListener) {
				elem.removeEventListener(type, handle);
			}
		};

		jQuery.Event = function (src, props) {

			// Allow instantiation without the 'new' keyword
			if (!(this instanceof jQuery.Event)) {
				return new jQuery.Event(src, props);
			}

			// Event object
			if (src && src.type) {
				this.originalEvent = src;
				this.type = src.type;

				// Events bubbling up the document may have been marked as prevented
				// by a handler lower down the tree; reflect the correct value.
				this.isDefaultPrevented = src.defaultPrevented ||
					src.defaultPrevented === undefined &&

					// Support: Android <=2.3 only
					src.returnValue === false ?
					returnTrue :
					returnFalse;

				// Create target properties
				// Support: Safari <=6 - 7 only
				// Target should not be a text node (#504, #13143)
				this.target = (src.target && src.target.nodeType === 3) ?
					src.target.parentNode :
					src.target;

				this.currentTarget = src.currentTarget;
				this.relatedTarget = src.relatedTarget;

				// Event type
			} else {
				this.type = src;
			}

			// Put explicitly provided properties onto the event object
			if (props) {
				jQuery.extend(this, props);
			}

			// Create a timestamp if incoming event doesn't have one
			this.timeStamp = src && src.timeStamp || jQuery.now();

			// Mark it as fixed
			this[jQuery.expando] = true;
		};

		// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
		// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
		jQuery.Event.prototype = {
			constructor: jQuery.Event,
			isDefaultPrevented: returnFalse,
			isPropagationStopped: returnFalse,
			isImmediatePropagationStopped: returnFalse,
			isSimulated: false,

			preventDefault: function () {
				var e = this.originalEvent;

				this.isDefaultPrevented = returnTrue;

				if (e && !this.isSimulated) {
					e.preventDefault();
				}
			},
			stopPropagation: function () {
				var e = this.originalEvent;

				this.isPropagationStopped = returnTrue;

				if (e && !this.isSimulated) {
					e.stopPropagation();
				}
			},
			stopImmediatePropagation: function () {
				var e = this.originalEvent;

				this.isImmediatePropagationStopped = returnTrue;

				if (e && !this.isSimulated) {
					e.stopImmediatePropagation();
				}

				this.stopPropagation();
			}
		};

		// Includes all common event props including KeyEvent and MouseEvent specific props
		jQuery.each({
			altKey: true,
			bubbles: true,
			cancelable: true,
			changedTouches: true,
			ctrlKey: true,
			detail: true,
			eventPhase: true,
			metaKey: true,
			pageX: true,
			pageY: true,
			shiftKey: true,
			view: true,
			"char": true,
			charCode: true,
			key: true,
			keyCode: true,
			button: true,
			buttons: true,
			clientX: true,
			clientY: true,
			offsetX: true,
			offsetY: true,
			pointerId: true,
			pointerType: true,
			screenX: true,
			screenY: true,
			targetTouches: true,
			toElement: true,
			touches: true,

			which: function (event) {
				var button = event.button;

				// Add which for key events
				if (event.which == null && rkeyEvent.test(event.type)) {
					return event.charCode != null ? event.charCode : event.keyCode;
				}

				// Add which for click: 1 === left; 2 === middle; 3 === right
				if (!event.which && button !== undefined && rmouseEvent.test(event.type)) {
					if (button & 1) {
						return 1;
					}

					if (button & 2) {
						return 3;
					}

					if (button & 4) {
						return 2;
					}

					return 0;
				}

				return event.which;
			}
		}, jQuery.event.addProp);

		// Create mouseenter/leave events using mouseover/out and event-time checks
		// so that event delegation works in jQuery.
		// Do the same for pointerenter/pointerleave and pointerover/pointerout
		//
		// Support: Safari 7 only
		// Safari sends mouseenter too often; see:
		// https://bugs.chromium.org/p/chromium/issues/detail?id=470258
		// for the description of the bug (it existed in older Chrome versions as well).
		jQuery.each({
			mouseenter: "mouseover",
			mouseleave: "mouseout",
			pointerenter: "pointerover",
			pointerleave: "pointerout"
		}, function (orig, fix) {
			jQuery.event.special[orig] = {
				delegateType: fix,
				bindType: fix,

				handle: function (event) {
					var ret,
						target = this,
						related = event.relatedTarget,
						handleObj = event.handleObj;

					// For mouseenter/leave call the handler if related is outside the target.
					// NB: No relatedTarget if the mouse left/entered the browser window
					if (!related || (related !== target && !jQuery.contains(target, related))) {
						event.type = handleObj.origType;
						ret = handleObj.handler.apply(this, arguments);
						event.type = fix;
					}
					return ret;
				}
			};
		});

		jQuery.fn.extend({

			on: function (types, selector, data, fn) {
				return on(this, types, selector, data, fn);
			},
			one: function (types, selector, data, fn) {
				return on(this, types, selector, data, fn, 1);
			},
			off: function (types, selector, fn) {
				var this$1 = this;

				var handleObj, type;
				if (types && types.preventDefault && types.handleObj) {

					// ( event )  dispatched jQuery.Event
					handleObj = types.handleObj;
					jQuery(types.delegateTarget).off(
						handleObj.namespace ?
							handleObj.origType + "." + handleObj.namespace :
							handleObj.origType,
						handleObj.selector,
						handleObj.handler
					);
					return this;
				}
				if (typeof types === "object") {

					// ( types-object [, selector] )
					for (type in types) {
						this$1.off(type, selector, types[type]);
					}
					return this;
				}
				if (selector === false || typeof selector === "function") {

					// ( types [, fn] )
					fn = selector;
					selector = undefined;
				}
				if (fn === false) {
					fn = returnFalse;
				}
				return this.each(function () {
					jQuery.event.remove(this, types, fn, selector);
				});
			}
		});


		var

			/* eslint-disable max-len */

			// See https://github.com/eslint/eslint/issues/3229
			rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,

			/* eslint-enable */

			// Support: IE <=10 - 11, Edge 12 - 13
			// In IE/Edge using regex groups here causes severe slowdowns.
			// See https://connect.microsoft.com/IE/feedback/details/1736512/
			rnoInnerhtml = /<script|<style|<link/i,

			// checked="checked" or checked
			rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
			rscriptTypeMasked = /^true\/(.*)/,
			rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;

		// Prefer a tbody over its parent table for containing new rows
		function manipulationTarget(elem, content) {
			if (nodeName(elem, "table") &&
				nodeName(content.nodeType !== 11 ? content : content.firstChild, "tr")) {

				return jQuery(">tbody", elem)[0] || elem;
			}

			return elem;
		}

		// Replace/restore the type attribute of script elements for safe DOM manipulation
		function disableScript(elem) {
			elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
			return elem;
		}
		function restoreScript(elem) {
			var match = rscriptTypeMasked.exec(elem.type);

			if (match) {
				elem.type = match[1];
			} else {
				elem.removeAttribute("type");
			}

			return elem;
		}

		function cloneCopyEvent(src, dest) {
			var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;

			if (dest.nodeType !== 1) {
				return;
			}

			// 1. Copy private data: events, handlers, etc.
			if (dataPriv.hasData(src)) {
				pdataOld = dataPriv.access(src);
				pdataCur = dataPriv.set(dest, pdataOld);
				events = pdataOld.events;

				if (events) {
					delete pdataCur.handle;
					pdataCur.events = {};

					for (type in events) {
						for (i = 0, l = events[type].length; i < l; i++) {
							jQuery.event.add(dest, type, events[type][i]);
						}
					}
				}
			}

			// 2. Copy user data
			if (dataUser.hasData(src)) {
				udataOld = dataUser.access(src);
				udataCur = jQuery.extend({}, udataOld);

				dataUser.set(dest, udataCur);
			}
		}

		// Fix IE bugs, see support tests
		function fixInput(src, dest) {
			var nodeName = dest.nodeName.toLowerCase();

			// Fails to persist the checked state of a cloned checkbox or radio button.
			if (nodeName === "input" && rcheckableType.test(src.type)) {
				dest.checked = src.checked;

				// Fails to return the selected option to the default selected state when cloning options
			} else if (nodeName === "input" || nodeName === "textarea") {
				dest.defaultValue = src.defaultValue;
			}
		}

		function domManip(collection, args, callback, ignored) {

			// Flatten any nested arrays
			args = concat.apply([], args);

			var fragment, first, scripts, hasScripts, node, doc,
				i = 0,
				l = collection.length,
				iNoClone = l - 1,
				value = args[0],
				isFunction = jQuery.isFunction(value);

			// We can't cloneNode fragments that contain checked, in WebKit
			if (isFunction ||
				(l > 1 && typeof value === "string" &&
					!support.checkClone && rchecked.test(value))) {
				return collection.each(function (index) {
					var self = collection.eq(index);
					if (isFunction) {
						args[0] = value.call(this, index, self.html());
					}
					domManip(self, args, callback, ignored);
				});
			}

			if (l) {
				fragment = buildFragment(args, collection[0].ownerDocument, false, collection, ignored);
				first = fragment.firstChild;

				if (fragment.childNodes.length === 1) {
					fragment = first;
				}

				// Require either new content or an interest in ignored elements to invoke the callback
				if (first || ignored) {
					scripts = jQuery.map(getAll(fragment, "script"), disableScript);
					hasScripts = scripts.length;

					// Use the original fragment for the last item
					// instead of the first because it can end up
					// being emptied incorrectly in certain situations (#8070).
					for (; i < l; i++) {
						node = fragment;

						if (i !== iNoClone) {
							node = jQuery.clone(node, true, true);

							// Keep references to cloned scripts for later restoration
							if (hasScripts) {

								// Support: Android <=4.0 only, PhantomJS 1 only
								// push.apply(_, arraylike) throws on ancient WebKit
								jQuery.merge(scripts, getAll(node, "script"));
							}
						}

						callback.call(collection[i], node, i);
					}

					if (hasScripts) {
						doc = scripts[scripts.length - 1].ownerDocument;

						// Reenable scripts
						jQuery.map(scripts, restoreScript);

						// Evaluate executable scripts on first document insertion
						for (i = 0; i < hasScripts; i++) {
							node = scripts[i];
							if (rscriptType.test(node.type || "") &&
								!dataPriv.access(node, "globalEval") &&
								jQuery.contains(doc, node)) {

								if (node.src) {

									// Optional AJAX dependency, but won't run scripts if not present
									if (jQuery._evalUrl) {
										jQuery._evalUrl(node.src);
									}
								} else {
									DOMEval(node.textContent.replace(rcleanScript, ""), doc);
								}
							}
						}
					}
				}
			}

			return collection;
		}

		function remove(elem, selector, keepData) {
			var node,
				nodes = selector ? jQuery.filter(selector, elem) : elem,
				i = 0;

			for (; (node = nodes[i]) != null; i++) {
				if (!keepData && node.nodeType === 1) {
					jQuery.cleanData(getAll(node));
				}

				if (node.parentNode) {
					if (keepData && jQuery.contains(node.ownerDocument, node)) {
						setGlobalEval(getAll(node, "script"));
					}
					node.parentNode.removeChild(node);
				}
			}

			return elem;
		}

		jQuery.extend({
			htmlPrefilter: function (html) {
				return html.replace(rxhtmlTag, "<$1></$2>");
			},

			clone: function (elem, dataAndEvents, deepDataAndEvents) {
				var i, l, srcElements, destElements,
					clone = elem.cloneNode(true),
					inPage = jQuery.contains(elem.ownerDocument, elem);

				// Fix IE cloning issues
				if (!support.noCloneChecked && (elem.nodeType === 1 || elem.nodeType === 11) &&
					!jQuery.isXMLDoc(elem)) {

					// We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
					destElements = getAll(clone);
					srcElements = getAll(elem);

					for (i = 0, l = srcElements.length; i < l; i++) {
						fixInput(srcElements[i], destElements[i]);
					}
				}

				// Copy the events from the original to the clone
				if (dataAndEvents) {
					if (deepDataAndEvents) {
						srcElements = srcElements || getAll(elem);
						destElements = destElements || getAll(clone);

						for (i = 0, l = srcElements.length; i < l; i++) {
							cloneCopyEvent(srcElements[i], destElements[i]);
						}
					} else {
						cloneCopyEvent(elem, clone);
					}
				}

				// Preserve script evaluation history
				destElements = getAll(clone, "script");
				if (destElements.length > 0) {
					setGlobalEval(destElements, !inPage && getAll(elem, "script"));
				}

				// Return the cloned set
				return clone;
			},

			cleanData: function (elems) {
				var data, elem, type,
					special = jQuery.event.special,
					i = 0;

				for (; (elem = elems[i]) !== undefined; i++) {
					if (acceptData(elem)) {
						if ((data = elem[dataPriv.expando])) {
							if (data.events) {
								for (type in data.events) {
									if (special[type]) {
										jQuery.event.remove(elem, type);

										// This is a shortcut to avoid jQuery.event.remove's overhead
									} else {
										jQuery.removeEvent(elem, type, data.handle);
									}
								}
							}

							// Support: Chrome <=35 - 45+
							// Assign undefined instead of using delete, see Data#remove
							elem[dataPriv.expando] = undefined;
						}
						if (elem[dataUser.expando]) {

							// Support: Chrome <=35 - 45+
							// Assign undefined instead of using delete, see Data#remove
							elem[dataUser.expando] = undefined;
						}
					}
				}
			}
		});

		jQuery.fn.extend({
			detach: function (selector) {
				return remove(this, selector, true);
			},

			remove: function (selector) {
				return remove(this, selector);
			},

			text: function (value) {
				return access(this, function (value) {
					return value === undefined ?
						jQuery.text(this) :
						this.empty().each(function () {
							if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
								this.textContent = value;
							}
						});
				}, null, value, arguments.length);
			},

			append: function () {
				return domManip(this, arguments, function (elem) {
					if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
						var target = manipulationTarget(this, elem);
						target.appendChild(elem);
					}
				});
			},

			prepend: function () {
				return domManip(this, arguments, function (elem) {
					if (this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9) {
						var target = manipulationTarget(this, elem);
						target.insertBefore(elem, target.firstChild);
					}
				});
			},

			before: function () {
				return domManip(this, arguments, function (elem) {
					if (this.parentNode) {
						this.parentNode.insertBefore(elem, this);
					}
				});
			},

			after: function () {
				return domManip(this, arguments, function (elem) {
					if (this.parentNode) {
						this.parentNode.insertBefore(elem, this.nextSibling);
					}
				});
			},

			empty: function () {
				var elem,
					i = 0;

				for (; (elem = this[i]) != null; i++) {
					if (elem.nodeType === 1) {

						// Prevent memory leaks
						jQuery.cleanData(getAll(elem, false));

						// Remove any remaining nodes
						elem.textContent = "";
					}
				}

				return this;
			},

			clone: function (dataAndEvents, deepDataAndEvents) {
				dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
				deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

				return this.map(function () {
					return jQuery.clone(this, dataAndEvents, deepDataAndEvents);
				});
			},

			html: function (value) {
				return access(this, function (value) {
					var this$1 = this;

					var elem = this[0] || {},
						i = 0,
						l = this.length;

					if (value === undefined && elem.nodeType === 1) {
						return elem.innerHTML;
					}

					// See if we can take a shortcut and just use innerHTML
					if (typeof value === "string" && !rnoInnerhtml.test(value) &&
						!wrapMap[(rtagName.exec(value) || ["", ""])[1].toLowerCase()]) {

						value = jQuery.htmlPrefilter(value);

						try {
							for (; i < l; i++) {
								elem = this$1[i] || {};

								// Remove element nodes and prevent memory leaks
								if (elem.nodeType === 1) {
									jQuery.cleanData(getAll(elem, false));
									elem.innerHTML = value;
								}
							}

							elem = 0;

							// If using innerHTML throws an exception, use the fallback method
						} catch (e) { }
					}

					if (elem) {
						this.empty().append(value);
					}
				}, null, value, arguments.length);
			},

			replaceWith: function () {
				var ignored = [];

				// Make the changes, replacing each non-ignored context element with the new content
				return domManip(this, arguments, function (elem) {
					var parent = this.parentNode;

					if (jQuery.inArray(this, ignored) < 0) {
						jQuery.cleanData(getAll(this));
						if (parent) {
							parent.replaceChild(elem, this);
						}
					}

					// Force callback invocation
				}, ignored);
			}
		});

		jQuery.each({
			appendTo: "append",
			prependTo: "prepend",
			insertBefore: "before",
			insertAfter: "after",
			replaceAll: "replaceWith"
		}, function (name, original) {
			jQuery.fn[name] = function (selector) {
				var this$1 = this;

				var elems,
					ret = [],
					insert = jQuery(selector),
					last = insert.length - 1,
					i = 0;

				for (; i <= last; i++) {
					elems = i === last ? this$1 : this$1.clone(true);
					jQuery(insert[i])[original](elems);

					// Support: Android <=4.0 only, PhantomJS 1 only
					// .get() because push.apply(_, arraylike) throws on ancient WebKit
					push.apply(ret, elems.get());
				}

				return this.pushStack(ret);
			};
		});
		var rmargin = (/^margin/);

		var rnumnonpx = new RegExp("^(" + pnum + ")(?!px)[a-z%]+$", "i");

		var getStyles = function (elem) {

			// Support: IE <=11 only, Firefox <=30 (#15098, #14150)
			// IE throws on elements created in popups
			// FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
			var view = elem.ownerDocument.defaultView;

			if (!view || !view.opener) {
				view = window;
			}

			return view.getComputedStyle(elem);
		};



		(function () {

			// Executing both pixelPosition & boxSizingReliable tests require only one layout
			// so they're executed at the same time to save the second computation.
			function computeStyleTests() {

				// This is a singleton, we need to execute it only once
				if (!div) {
					return;
				}

				div.style.cssText =
					"box-sizing:border-box;" +
					"position:relative;display:block;" +
					"margin:auto;border:1px;padding:1px;" +
					"top:1%;width:50%";
				div.innerHTML = "";
				documentElement.appendChild(container);

				var divStyle = window.getComputedStyle(div);
				pixelPositionVal = divStyle.top !== "1%";

				// Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
				reliableMarginLeftVal = divStyle.marginLeft === "2px";
				boxSizingReliableVal = divStyle.width === "4px";

				// Support: Android 4.0 - 4.3 only
				// Some styles come back with percentage values, even though they shouldn't
				div.style.marginRight = "50%";
				pixelMarginRightVal = divStyle.marginRight === "4px";

				documentElement.removeChild(container);

				// Nullify the div so it wouldn't be stored in the memory and
				// it will also be a sign that checks already performed
				div = null;
			}

			var pixelPositionVal, boxSizingReliableVal, pixelMarginRightVal, reliableMarginLeftVal,
				container = document.createElement("div"),
				div = document.createElement("div");

			// Finish early in limited (non-browser) environments
			if (!div.style) {
				return;
			}

			// Support: IE <=9 - 11 only
			// Style of cloned element affects source element cloned (#8908)
			div.style.backgroundClip = "content-box";
			div.cloneNode(true).style.backgroundClip = "";
			support.clearCloneStyle = div.style.backgroundClip === "content-box";

			container.style.cssText = "border:0;width:8px;height:0;top:0;left:-9999px;" +
				"padding:0;margin-top:1px;position:absolute";
			container.appendChild(div);

			jQuery.extend(support, {
				pixelPosition: function () {
					computeStyleTests();
					return pixelPositionVal;
				},
				boxSizingReliable: function () {
					computeStyleTests();
					return boxSizingReliableVal;
				},
				pixelMarginRight: function () {
					computeStyleTests();
					return pixelMarginRightVal;
				},
				reliableMarginLeft: function () {
					computeStyleTests();
					return reliableMarginLeftVal;
				}
			});
		})();


		function curCSS(elem, name, computed) {
			var width, minWidth, maxWidth, ret,

				// Support: Firefox 51+
				// Retrieving style before computed somehow
				// fixes an issue with getting wrong values
				// on detached elements
				style = elem.style;

			computed = computed || getStyles(elem);

			// getPropertyValue is needed for:
			//   .css('filter') (IE 9 only, #12537)
			//   .css('--customProperty) (#3144)
			if (computed) {
				ret = computed.getPropertyValue(name) || computed[name];

				if (ret === "" && !jQuery.contains(elem.ownerDocument, elem)) {
					ret = jQuery.style(elem, name);
				}

				// A tribute to the "awesome hack by Dean Edwards"
				// Android Browser returns percentage for some values,
				// but width seems to be reliably pixels.
				// This is against the CSSOM draft spec:
				// https://drafts.csswg.org/cssom/#resolved-values
				if (!support.pixelMarginRight() && rnumnonpx.test(ret) && rmargin.test(name)) {

					// Remember the original values
					width = style.width;
					minWidth = style.minWidth;
					maxWidth = style.maxWidth;

					// Put in the new values to get a computed value out
					style.minWidth = style.maxWidth = style.width = ret;
					ret = computed.width;

					// Revert the changed values
					style.width = width;
					style.minWidth = minWidth;
					style.maxWidth = maxWidth;
				}
			}

			return ret !== undefined ?

				// Support: IE <=9 - 11 only
				// IE returns zIndex value as an integer.
				ret + "" :
				ret;
		}


		function addGetHookIf(conditionFn, hookFn) {

			// Define the hook, we'll check on the first run if it's really needed.
			return {
				get: function () {
					if (conditionFn()) {

						// Hook not needed (or it's not possible to use it due
						// to missing dependency), remove it.
						delete this.get;
						return;
					}

					// Hook needed; redefine it so that the support test is not executed again.
					return (this.get = hookFn).apply(this, arguments);
				}
			};
		}


		var

			// Swappable if display is none or starts with table
			// except "table", "table-cell", or "table-caption"
			// See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
			rdisplayswap = /^(none|table(?!-c[ea]).+)/,
			rcustomProp = /^--/,
			cssShow = { position: "absolute", visibility: "hidden", display: "block" },
			cssNormalTransform = {
				letterSpacing: "0",
				fontWeight: "400"
			},

			cssPrefixes = ["Webkit", "Moz", "ms"],
			emptyStyle = document.createElement("div").style;

		// Return a css property mapped to a potentially vendor prefixed property
		function vendorPropName(name) {

			// Shortcut for names that are not vendor prefixed
			if (name in emptyStyle) {
				return name;
			}

			// Check for vendor prefixed names
			var capName = name[0].toUpperCase() + name.slice(1),
				i = cssPrefixes.length;

			while (i--) {
				name = cssPrefixes[i] + capName;
				if (name in emptyStyle) {
					return name;
				}
			}
		}

		// Return a property mapped along what jQuery.cssProps suggests or to
		// a vendor prefixed property.
		function finalPropName(name) {
			var ret = jQuery.cssProps[name];
			if (!ret) {
				ret = jQuery.cssProps[name] = vendorPropName(name) || name;
			}
			return ret;
		}

		function setPositiveNumber(elem, value, subtract) {

			// Any relative (+/-) values have already been
			// normalized at this point
			var matches = rcssNum.exec(value);
			return matches ?

				// Guard against undefined "subtract", e.g., when used as in cssHooks
				Math.max(0, matches[2] - (subtract || 0)) + (matches[3] || "px") :
				value;
		}

		function augmentWidthOrHeight(elem, name, extra, isBorderBox, styles) {
			var i,
				val = 0;

			// If we already have the right measurement, avoid augmentation
			if (extra === (isBorderBox ? "border" : "content")) {
				i = 4;

				// Otherwise initialize for horizontal or vertical properties
			} else {
				i = name === "width" ? 1 : 0;
			}

			for (; i < 4; i += 2) {

				// Both box models exclude margin, so add it if we want it
				if (extra === "margin") {
					val += jQuery.css(elem, extra + cssExpand[i], true, styles);
				}

				if (isBorderBox) {

					// border-box includes padding, so remove it if we want content
					if (extra === "content") {
						val -= jQuery.css(elem, "padding" + cssExpand[i], true, styles);
					}

					// At this point, extra isn't border nor margin, so remove border
					if (extra !== "margin") {
						val -= jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles);
					}
				} else {

					// At this point, extra isn't content, so add padding
					val += jQuery.css(elem, "padding" + cssExpand[i], true, styles);

					// At this point, extra isn't content nor padding, so add border
					if (extra !== "padding") {
						val += jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles);
					}
				}
			}

			return val;
		}

		function getWidthOrHeight(elem, name, extra) {

			// Start with computed style
			var valueIsBorderBox,
				styles = getStyles(elem),
				val = curCSS(elem, name, styles),
				isBorderBox = jQuery.css(elem, "boxSizing", false, styles) === "border-box";

			// Computed unit is not pixels. Stop here and return.
			if (rnumnonpx.test(val)) {
				return val;
			}

			// Check for style in case a browser which returns unreliable values
			// for getComputedStyle silently falls back to the reliable elem.style
			valueIsBorderBox = isBorderBox &&
				(support.boxSizingReliable() || val === elem.style[name]);

			// Fall back to offsetWidth/Height when value is "auto"
			// This happens for inline elements with no explicit setting (gh-3571)
			if (val === "auto") {
				val = elem["offset" + name[0].toUpperCase() + name.slice(1)];
			}

			// Normalize "", auto, and prepare for extra
			val = parseFloat(val) || 0;

			// Use the active box-sizing model to add/subtract irrelevant styles
			return (val +
				augmentWidthOrHeight(
					elem,
					name,
					extra || (isBorderBox ? "border" : "content"),
					valueIsBorderBox,
					styles
				)
			) + "px";
		}

		jQuery.extend({

			// Add in style property hooks for overriding the default
			// behavior of getting and setting a style property
			cssHooks: {
				opacity: {
					get: function (elem, computed) {
						if (computed) {

							// We should always get a number back from opacity
							var ret = curCSS(elem, "opacity");
							return ret === "" ? "1" : ret;
						}
					}
				}
			},

			// Don't automatically add "px" to these possibly-unitless properties
			cssNumber: {
				"animationIterationCount": true,
				"columnCount": true,
				"fillOpacity": true,
				"flexGrow": true,
				"flexShrink": true,
				"fontWeight": true,
				"lineHeight": true,
				"opacity": true,
				"order": true,
				"orphans": true,
				"widows": true,
				"zIndex": true,
				"zoom": true
			},

			// Add in properties whose names you wish to fix before
			// setting or getting the value
			cssProps: {
				"float": "cssFloat"
			},

			// Get and set the style property on a DOM Node
			style: function (elem, name, value, extra) {

				// Don't set styles on text and comment nodes
				if (!elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style) {
					return;
				}

				// Make sure that we're working with the right name
				var ret, type, hooks,
					origName = jQuery.camelCase(name),
					isCustomProp = rcustomProp.test(name),
					style = elem.style;

				// Make sure that we're working with the right name. We don't
				// want to query the value if it is a CSS custom property
				// since they are user-defined.
				if (!isCustomProp) {
					name = finalPropName(origName);
				}

				// Gets hook for the prefixed version, then unprefixed version
				hooks = jQuery.cssHooks[name] || jQuery.cssHooks[origName];

				// Check if we're setting a value
				if (value !== undefined) {
					type = typeof value;

					// Convert "+=" or "-=" to relative numbers (#7345)
					if (type === "string" && (ret = rcssNum.exec(value)) && ret[1]) {
						value = adjustCSS(elem, name, ret);

						// Fixes bug #9237
						type = "number";
					}

					// Make sure that null and NaN values aren't set (#7116)
					if (value == null || value !== value) {
						return;
					}

					// If a number was passed in, add the unit (except for certain CSS properties)
					if (type === "number") {
						value += ret && ret[3] || (jQuery.cssNumber[origName] ? "" : "px");
					}

					// background-* props affect original clone's values
					if (!support.clearCloneStyle && value === "" && name.indexOf("background") === 0) {
						style[name] = "inherit";
					}

					// If a hook was provided, use that value, otherwise just set the specified value
					if (!hooks || !("set" in hooks) ||
						(value = hooks.set(elem, value, extra)) !== undefined) {

						if (isCustomProp) {
							style.setProperty(name, value);
						} else {
							style[name] = value;
						}
					}

				} else {

					// If a hook was provided get the non-computed value from there
					if (hooks && "get" in hooks &&
						(ret = hooks.get(elem, false, extra)) !== undefined) {

						return ret;
					}

					// Otherwise just get the value from the style object
					return style[name];
				}
			},

			css: function (elem, name, extra, styles) {
				var val, num, hooks,
					origName = jQuery.camelCase(name),
					isCustomProp = rcustomProp.test(name);

				// Make sure that we're working with the right name. We don't
				// want to modify the value if it is a CSS custom property
				// since they are user-defined.
				if (!isCustomProp) {
					name = finalPropName(origName);
				}

				// Try prefixed name followed by the unprefixed name
				hooks = jQuery.cssHooks[name] || jQuery.cssHooks[origName];

				// If a hook was provided get the computed value from there
				if (hooks && "get" in hooks) {
					val = hooks.get(elem, true, extra);
				}

				// Otherwise, if a way to get the computed value exists, use that
				if (val === undefined) {
					val = curCSS(elem, name, styles);
				}

				// Convert "normal" to computed value
				if (val === "normal" && name in cssNormalTransform) {
					val = cssNormalTransform[name];
				}

				// Make numeric if forced or a qualifier was provided and val looks numeric
				if (extra === "" || extra) {
					num = parseFloat(val);
					return extra === true || isFinite(num) ? num || 0 : val;
				}

				return val;
			}
		});

		jQuery.each(["height", "width"], function (i, name) {
			jQuery.cssHooks[name] = {
				get: function (elem, computed, extra) {
					if (computed) {

						// Certain elements can have dimension info if we invisibly show them
						// but it must have a current display style that would benefit
						return rdisplayswap.test(jQuery.css(elem, "display")) &&

							// Support: Safari 8+
							// Table columns in Safari have non-zero offsetWidth & zero
							// getBoundingClientRect().width unless display is changed.
							// Support: IE <=11 only
							// Running getBoundingClientRect on a disconnected node
							// in IE throws an error.
							(!elem.getClientRects().length || !elem.getBoundingClientRect().width) ?
							swap(elem, cssShow, function () {
								return getWidthOrHeight(elem, name, extra);
							}) :
							getWidthOrHeight(elem, name, extra);
					}
				},

				set: function (elem, value, extra) {
					var matches,
						styles = extra && getStyles(elem),
						subtract = extra && augmentWidthOrHeight(
							elem,
							name,
							extra,
							jQuery.css(elem, "boxSizing", false, styles) === "border-box",
							styles
						);

					// Convert to pixels if value adjustment is needed
					if (subtract && (matches = rcssNum.exec(value)) &&
						(matches[3] || "px") !== "px") {

						elem.style[name] = value;
						value = jQuery.css(elem, name);
					}

					return setPositiveNumber(elem, value, subtract);
				}
			};
		});

		jQuery.cssHooks.marginLeft = addGetHookIf(support.reliableMarginLeft,
			function (elem, computed) {
				if (computed) {
					return (parseFloat(curCSS(elem, "marginLeft")) ||
						elem.getBoundingClientRect().left -
						swap(elem, { marginLeft: 0 }, function () {
							return elem.getBoundingClientRect().left;
						})
					) + "px";
				}
			}
		);

		// These hooks are used by animate to expand properties
		jQuery.each({
			margin: "",
			padding: "",
			border: "Width"
		}, function (prefix, suffix) {
			jQuery.cssHooks[prefix + suffix] = {
				expand: function (value) {
					var i = 0,
						expanded = {},

						// Assumes a single number if not a string
						parts = typeof value === "string" ? value.split(" ") : [value];

					for (; i < 4; i++) {
						expanded[prefix + cssExpand[i] + suffix] =
							parts[i] || parts[i - 2] || parts[0];
					}

					return expanded;
				}
			};

			if (!rmargin.test(prefix)) {
				jQuery.cssHooks[prefix + suffix].set = setPositiveNumber;
			}
		});

		jQuery.fn.extend({
			css: function (name, value) {
				return access(this, function (elem, name, value) {
					var styles, len,
						map = {},
						i = 0;

					if (Array.isArray(name)) {
						styles = getStyles(elem);
						len = name.length;

						for (; i < len; i++) {
							map[name[i]] = jQuery.css(elem, name[i], false, styles);
						}

						return map;
					}

					return value !== undefined ?
						jQuery.style(elem, name, value) :
						jQuery.css(elem, name);
				}, name, value, arguments.length > 1);
			}
		});


		function Tween(elem, options, prop, end, easing) {
			return new Tween.prototype.init(elem, options, prop, end, easing);
		}
		jQuery.Tween = Tween;

		Tween.prototype = {
			constructor: Tween,
			init: function (elem, options, prop, end, easing, unit) {
				this.elem = elem;
				this.prop = prop;
				this.easing = easing || jQuery.easing._default;
				this.options = options;
				this.start = this.now = this.cur();
				this.end = end;
				this.unit = unit || (jQuery.cssNumber[prop] ? "" : "px");
			},
			cur: function () {
				var hooks = Tween.propHooks[this.prop];

				return hooks && hooks.get ?
					hooks.get(this) :
					Tween.propHooks._default.get(this);
			},
			run: function (percent) {
				var eased,
					hooks = Tween.propHooks[this.prop];

				if (this.options.duration) {
					this.pos = eased = jQuery.easing[this.easing](
						percent, this.options.duration * percent, 0, 1, this.options.duration
					);
				} else {
					this.pos = eased = percent;
				}
				this.now = (this.end - this.start) * eased + this.start;

				if (this.options.step) {
					this.options.step.call(this.elem, this.now, this);
				}

				if (hooks && hooks.set) {
					hooks.set(this);
				} else {
					Tween.propHooks._default.set(this);
				}
				return this;
			}
		};

		Tween.prototype.init.prototype = Tween.prototype;

		Tween.propHooks = {
			_default: {
				get: function (tween) {
					var result;

					// Use a property on the element directly when it is not a DOM element,
					// or when there is no matching style property that exists.
					if (tween.elem.nodeType !== 1 ||
						tween.elem[tween.prop] != null && tween.elem.style[tween.prop] == null) {
						return tween.elem[tween.prop];
					}

					// Passing an empty string as a 3rd parameter to .css will automatically
					// attempt a parseFloat and fallback to a string if the parse fails.
					// Simple values such as "10px" are parsed to Float;
					// complex values such as "rotate(1rad)" are returned as-is.
					result = jQuery.css(tween.elem, tween.prop, "");

					// Empty strings, null, undefined and "auto" are converted to 0.
					return !result || result === "auto" ? 0 : result;
				},
				set: function (tween) {

					// Use step hook for back compat.
					// Use cssHook if its there.
					// Use .style if available and use plain properties where available.
					if (jQuery.fx.step[tween.prop]) {
						jQuery.fx.step[tween.prop](tween);
					} else if (tween.elem.nodeType === 1 &&
						(tween.elem.style[jQuery.cssProps[tween.prop]] != null ||
							jQuery.cssHooks[tween.prop])) {
						jQuery.style(tween.elem, tween.prop, tween.now + tween.unit);
					} else {
						tween.elem[tween.prop] = tween.now;
					}
				}
			}
		};

		// Support: IE <=9 only
		// Panic based approach to setting things on disconnected nodes
		Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
			set: function (tween) {
				if (tween.elem.nodeType && tween.elem.parentNode) {
					tween.elem[tween.prop] = tween.now;
				}
			}
		};

		jQuery.easing = {
			linear: function (p) {
				return p;
			},
			swing: function (p) {
				return 0.5 - Math.cos(p * Math.PI) / 2;
			},
			_default: "swing"
		};

		jQuery.fx = Tween.prototype.init;

		// Back compat <1.8 extension point
		jQuery.fx.step = {};




		var
			fxNow, inProgress,
			rfxtypes = /^(?:toggle|show|hide)$/,
			rrun = /queueHooks$/;

		function schedule() {
			if (inProgress) {
				if (document.hidden === false && window.requestAnimationFrame) {
					window.requestAnimationFrame(schedule);
				} else {
					window.setTimeout(schedule, jQuery.fx.interval);
				}

				jQuery.fx.tick();
			}
		}

		// Animations created synchronously will run synchronously
		function createFxNow() {
			window.setTimeout(function () {
				fxNow = undefined;
			});
			return (fxNow = jQuery.now());
		}

		// Generate parameters to create a standard animation
		function genFx(type, includeWidth) {
			var which,
				i = 0,
				attrs = { height: type };

			// If we include width, step value is 1 to do all cssExpand values,
			// otherwise step value is 2 to skip over Left and Right
			includeWidth = includeWidth ? 1 : 0;
			for (; i < 4; i += 2 - includeWidth) {
				which = cssExpand[i];
				attrs["margin" + which] = attrs["padding" + which] = type;
			}

			if (includeWidth) {
				attrs.opacity = attrs.width = type;
			}

			return attrs;
		}

		function createTween(value, prop, animation) {
			var tween,
				collection = (Animation.tweeners[prop] || []).concat(Animation.tweeners["*"]),
				index = 0,
				length = collection.length;
			for (; index < length; index++) {
				if ((tween = collection[index].call(animation, prop, value))) {

					// We're done with this property
					return tween;
				}
			}
		}

		function defaultPrefilter(elem, props, opts) {
			var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
				isBox = "width" in props || "height" in props,
				anim = this,
				orig = {},
				style = elem.style,
				hidden = elem.nodeType && isHiddenWithinTree(elem),
				dataShow = dataPriv.get(elem, "fxshow");

			// Queue-skipping animations hijack the fx hooks
			if (!opts.queue) {
				hooks = jQuery._queueHooks(elem, "fx");
				if (hooks.unqueued == null) {
					hooks.unqueued = 0;
					oldfire = hooks.empty.fire;
					hooks.empty.fire = function () {
						if (!hooks.unqueued) {
							oldfire();
						}
					};
				}
				hooks.unqueued++;

				anim.always(function () {

					// Ensure the complete handler is called before this completes
					anim.always(function () {
						hooks.unqueued--;
						if (!jQuery.queue(elem, "fx").length) {
							hooks.empty.fire();
						}
					});
				});
			}

			// Detect show/hide animations
			for (prop in props) {
				value = props[prop];
				if (rfxtypes.test(value)) {
					delete props[prop];
					toggle = toggle || value === "toggle";
					if (value === (hidden ? "hide" : "show")) {

						// Pretend to be hidden if this is a "show" and
						// there is still data from a stopped show/hide
						if (value === "show" && dataShow && dataShow[prop] !== undefined) {
							hidden = true;

							// Ignore all other no-op show/hide data
						} else {
							continue;
						}
					}
					orig[prop] = dataShow && dataShow[prop] || jQuery.style(elem, prop);
				}
			}

			// Bail out if this is a no-op like .hide().hide()
			propTween = !jQuery.isEmptyObject(props);
			if (!propTween && jQuery.isEmptyObject(orig)) {
				return;
			}

			// Restrict "overflow" and "display" styles during box animations
			if (isBox && elem.nodeType === 1) {

				// Support: IE <=9 - 11, Edge 12 - 13
				// Record all 3 overflow attributes because IE does not infer the shorthand
				// from identically-valued overflowX and overflowY
				opts.overflow = [style.overflow, style.overflowX, style.overflowY];

				// Identify a display type, preferring old show/hide data over the CSS cascade
				restoreDisplay = dataShow && dataShow.display;
				if (restoreDisplay == null) {
					restoreDisplay = dataPriv.get(elem, "display");
				}
				display = jQuery.css(elem, "display");
				if (display === "none") {
					if (restoreDisplay) {
						display = restoreDisplay;
					} else {

						// Get nonempty value(s) by temporarily forcing visibility
						showHide([elem], true);
						restoreDisplay = elem.style.display || restoreDisplay;
						display = jQuery.css(elem, "display");
						showHide([elem]);
					}
				}

				// Animate inline elements as inline-block
				if (display === "inline" || display === "inline-block" && restoreDisplay != null) {
					if (jQuery.css(elem, "float") === "none") {

						// Restore the original display value at the end of pure show/hide animations
						if (!propTween) {
							anim.done(function () {
								style.display = restoreDisplay;
							});
							if (restoreDisplay == null) {
								display = style.display;
								restoreDisplay = display === "none" ? "" : display;
							}
						}
						style.display = "inline-block";
					}
				}
			}

			if (opts.overflow) {
				style.overflow = "hidden";
				anim.always(function () {
					style.overflow = opts.overflow[0];
					style.overflowX = opts.overflow[1];
					style.overflowY = opts.overflow[2];
				});
			}

			// Implement show/hide animations
			propTween = false;
			for (prop in orig) {

				// General show/hide setup for this element animation
				if (!propTween) {
					if (dataShow) {
						if ("hidden" in dataShow) {
							hidden = dataShow.hidden;
						}
					} else {
						dataShow = dataPriv.access(elem, "fxshow", { display: restoreDisplay });
					}

					// Store hidden/visible for toggle so `.stop().toggle()` "reverses"
					if (toggle) {
						dataShow.hidden = !hidden;
					}

					// Show elements before animating them
					if (hidden) {
						showHide([elem], true);
					}

					/* eslint-disable no-loop-func */

					anim.done(function () {

						/* eslint-enable no-loop-func */

						// The final step of a "hide" animation is actually hiding the element
						if (!hidden) {
							showHide([elem]);
						}
						dataPriv.remove(elem, "fxshow");
						for (prop in orig) {
							jQuery.style(elem, prop, orig[prop]);
						}
					});
				}

				// Per-property setup
				propTween = createTween(hidden ? dataShow[prop] : 0, prop, anim);
				if (!(prop in dataShow)) {
					dataShow[prop] = propTween.start;
					if (hidden) {
						propTween.end = propTween.start;
						propTween.start = 0;
					}
				}
			}
		}

		function propFilter(props, specialEasing) {
			var index, name, easing, value, hooks;

			// camelCase, specialEasing and expand cssHook pass
			for (index in props) {
				name = jQuery.camelCase(index);
				easing = specialEasing[name];
				value = props[index];
				if (Array.isArray(value)) {
					easing = value[1];
					value = props[index] = value[0];
				}

				if (index !== name) {
					props[name] = value;
					delete props[index];
				}

				hooks = jQuery.cssHooks[name];
				if (hooks && "expand" in hooks) {
					value = hooks.expand(value);
					delete props[name];

					// Not quite $.extend, this won't overwrite existing keys.
					// Reusing 'index' because we have the correct "name"
					for (index in value) {
						if (!(index in props)) {
							props[index] = value[index];
							specialEasing[index] = easing;
						}
					}
				} else {
					specialEasing[name] = easing;
				}
			}
		}

		function Animation(elem, properties, options) {
			var result,
				stopped,
				index = 0,
				length = Animation.prefilters.length,
				deferred = jQuery.Deferred().always(function () {

					// Don't match elem in the :animated selector
					delete tick.elem;
				}),
				tick = function () {
					if (stopped) {
						return false;
					}
					var currentTime = fxNow || createFxNow(),
						remaining = Math.max(0, animation.startTime + animation.duration - currentTime),

						// Support: Android 2.3 only
						// Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
						temp = remaining / animation.duration || 0,
						percent = 1 - temp,
						index = 0,
						length = animation.tweens.length;

					for (; index < length; index++) {
						animation.tweens[index].run(percent);
					}

					deferred.notifyWith(elem, [animation, percent, remaining]);

					// If there's more to do, yield
					if (percent < 1 && length) {
						return remaining;
					}

					// If this was an empty animation, synthesize a final progress notification
					if (!length) {
						deferred.notifyWith(elem, [animation, 1, 0]);
					}

					// Resolve the animation and report its conclusion
					deferred.resolveWith(elem, [animation]);
					return false;
				},
				animation = deferred.promise({
					elem: elem,
					props: jQuery.extend({}, properties),
					opts: jQuery.extend(true, {
						specialEasing: {},
						easing: jQuery.easing._default
					}, options),
					originalProperties: properties,
					originalOptions: options,
					startTime: fxNow || createFxNow(),
					duration: options.duration,
					tweens: [],
					createTween: function (prop, end) {
						var tween = jQuery.Tween(elem, animation.opts, prop, end,
							animation.opts.specialEasing[prop] || animation.opts.easing);
						animation.tweens.push(tween);
						return tween;
					},
					stop: function (gotoEnd) {
						var index = 0,

							// If we are going to the end, we want to run all the tweens
							// otherwise we skip this part
							length = gotoEnd ? animation.tweens.length : 0;
						if (stopped) {
							return this;
						}
						stopped = true;
						for (; index < length; index++) {
							animation.tweens[index].run(1);
						}

						// Resolve when we played the last frame; otherwise, reject
						if (gotoEnd) {
							deferred.notifyWith(elem, [animation, 1, 0]);
							deferred.resolveWith(elem, [animation, gotoEnd]);
						} else {
							deferred.rejectWith(elem, [animation, gotoEnd]);
						}
						return this;
					}
				}),
				props = animation.props;

			propFilter(props, animation.opts.specialEasing);

			for (; index < length; index++) {
				result = Animation.prefilters[index].call(animation, elem, props, animation.opts);
				if (result) {
					if (jQuery.isFunction(result.stop)) {
						jQuery._queueHooks(animation.elem, animation.opts.queue).stop =
							jQuery.proxy(result.stop, result);
					}
					return result;
				}
			}

			jQuery.map(props, createTween, animation);

			if (jQuery.isFunction(animation.opts.start)) {
				animation.opts.start.call(elem, animation);
			}

			// Attach callbacks from options
			animation
				.progress(animation.opts.progress)
				.done(animation.opts.done, animation.opts.complete)
				.fail(animation.opts.fail)
				.always(animation.opts.always);

			jQuery.fx.timer(
				jQuery.extend(tick, {
					elem: elem,
					anim: animation,
					queue: animation.opts.queue
				})
			);

			return animation;
		}

		jQuery.Animation = jQuery.extend(Animation, {

			tweeners: {
				"*": [function (prop, value) {
					var tween = this.createTween(prop, value);
					adjustCSS(tween.elem, prop, rcssNum.exec(value), tween);
					return tween;
				}]
			},

			tweener: function (props, callback) {
				if (jQuery.isFunction(props)) {
					callback = props;
					props = ["*"];
				} else {
					props = props.match(rnothtmlwhite);
				}

				var prop,
					index = 0,
					length = props.length;

				for (; index < length; index++) {
					prop = props[index];
					Animation.tweeners[prop] = Animation.tweeners[prop] || [];
					Animation.tweeners[prop].unshift(callback);
				}
			},

			prefilters: [defaultPrefilter],

			prefilter: function (callback, prepend) {
				if (prepend) {
					Animation.prefilters.unshift(callback);
				} else {
					Animation.prefilters.push(callback);
				}
			}
		});

		jQuery.speed = function (speed, easing, fn) {
			var opt = speed && typeof speed === "object" ? jQuery.extend({}, speed) : {
				complete: fn || !fn && easing ||
					jQuery.isFunction(speed) && speed,
				duration: speed,
				easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
			};

			// Go to the end state if fx are off
			if (jQuery.fx.off) {
				opt.duration = 0;

			} else {
				if (typeof opt.duration !== "number") {
					if (opt.duration in jQuery.fx.speeds) {
						opt.duration = jQuery.fx.speeds[opt.duration];

					} else {
						opt.duration = jQuery.fx.speeds._default;
					}
				}
			}

			// Normalize opt.queue - true/undefined/null -> "fx"
			if (opt.queue == null || opt.queue === true) {
				opt.queue = "fx";
			}

			// Queueing
			opt.old = opt.complete;

			opt.complete = function () {
				if (jQuery.isFunction(opt.old)) {
					opt.old.call(this);
				}

				if (opt.queue) {
					jQuery.dequeue(this, opt.queue);
				}
			};

			return opt;
		};

		jQuery.fn.extend({
			fadeTo: function (speed, to, easing, callback) {

				// Show any hidden elements after setting opacity to 0
				return this.filter(isHiddenWithinTree).css("opacity", 0).show()

					// Animate to the value specified
					.end().animate({ opacity: to }, speed, easing, callback);
			},
			animate: function (prop, speed, easing, callback) {
				var empty = jQuery.isEmptyObject(prop),
					optall = jQuery.speed(speed, easing, callback),
					doAnimation = function () {

						// Operate on a copy of prop so per-property easing won't be lost
						var anim = Animation(this, jQuery.extend({}, prop), optall);

						// Empty animations, or finishing resolves immediately
						if (empty || dataPriv.get(this, "finish")) {
							anim.stop(true);
						}
					};
				doAnimation.finish = doAnimation;

				return empty || optall.queue === false ?
					this.each(doAnimation) :
					this.queue(optall.queue, doAnimation);
			},
			stop: function (type, clearQueue, gotoEnd) {
				var stopQueue = function (hooks) {
					var stop = hooks.stop;
					delete hooks.stop;
					stop(gotoEnd);
				};

				if (typeof type !== "string") {
					gotoEnd = clearQueue;
					clearQueue = type;
					type = undefined;
				}
				if (clearQueue && type !== false) {
					this.queue(type || "fx", []);
				}

				return this.each(function () {
					var this$1 = this;

					var dequeue = true,
						index = type != null && type + "queueHooks",
						timers = jQuery.timers,
						data = dataPriv.get(this);

					if (index) {
						if (data[index] && data[index].stop) {
							stopQueue(data[index]);
						}
					} else {
						for (index in data) {
							if (data[index] && data[index].stop && rrun.test(index)) {
								stopQueue(data[index]);
							}
						}
					}

					for (index = timers.length; index--;) {
						if (timers[index].elem === this$1 &&
							(type == null || timers[index].queue === type)) {

							timers[index].anim.stop(gotoEnd);
							dequeue = false;
							timers.splice(index, 1);
						}
					}

					// Start the next in the queue if the last step wasn't forced.
					// Timers currently will call their complete callbacks, which
					// will dequeue but only if they were gotoEnd.
					if (dequeue || !gotoEnd) {
						jQuery.dequeue(this, type);
					}
				});
			},
			finish: function (type) {
				if (type !== false) {
					type = type || "fx";
				}
				return this.each(function () {
					var this$1 = this;

					var index,
						data = dataPriv.get(this),
						queue = data[type + "queue"],
						hooks = data[type + "queueHooks"],
						timers = jQuery.timers,
						length = queue ? queue.length : 0;

					// Enable finishing flag on private data
					data.finish = true;

					// Empty the queue first
					jQuery.queue(this, type, []);

					if (hooks && hooks.stop) {
						hooks.stop.call(this, true);
					}

					// Look for any active animations, and finish them
					for (index = timers.length; index--;) {
						if (timers[index].elem === this$1 && timers[index].queue === type) {
							timers[index].anim.stop(true);
							timers.splice(index, 1);
						}
					}

					// Look for any animations in the old queue and finish them
					for (index = 0; index < length; index++) {
						if (queue[index] && queue[index].finish) {
							queue[index].finish.call(this$1);
						}
					}

					// Turn off finishing flag
					delete data.finish;
				});
			}
		});

		jQuery.each(["toggle", "show", "hide"], function (i, name) {
			var cssFn = jQuery.fn[name];
			jQuery.fn[name] = function (speed, easing, callback) {
				return speed == null || typeof speed === "boolean" ?
					cssFn.apply(this, arguments) :
					this.animate(genFx(name, true), speed, easing, callback);
			};
		});

		// Generate shortcuts for custom animations
		jQuery.each({
			slideDown: genFx("show"),
			slideUp: genFx("hide"),
			slideToggle: genFx("toggle"),
			fadeIn: { opacity: "show" },
			fadeOut: { opacity: "hide" },
			fadeToggle: { opacity: "toggle" }
		}, function (name, props) {
			jQuery.fn[name] = function (speed, easing, callback) {
				return this.animate(props, speed, easing, callback);
			};
		});

		jQuery.timers = [];
		jQuery.fx.tick = function () {
			var timer,
				i = 0,
				timers = jQuery.timers;

			fxNow = jQuery.now();

			for (; i < timers.length; i++) {
				timer = timers[i];

				// Run the timer and safely remove it when done (allowing for external removal)
				if (!timer() && timers[i] === timer) {
					timers.splice(i--, 1);
				}
			}

			if (!timers.length) {
				jQuery.fx.stop();
			}
			fxNow = undefined;
		};

		jQuery.fx.timer = function (timer) {
			jQuery.timers.push(timer);
			jQuery.fx.start();
		};

		jQuery.fx.interval = 13;
		jQuery.fx.start = function () {
			if (inProgress) {
				return;
			}

			inProgress = true;
			schedule();
		};

		jQuery.fx.stop = function () {
			inProgress = null;
		};

		jQuery.fx.speeds = {
			slow: 600,
			fast: 200,

			// Default speed
			_default: 400
		};


		// Based off of the plugin by Clint Helfers, with permission.
		// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
		jQuery.fn.delay = function (time, type) {
			time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
			type = type || "fx";

			return this.queue(type, function (next, hooks) {
				var timeout = window.setTimeout(next, time);
				hooks.stop = function () {
					window.clearTimeout(timeout);
				};
			});
		};


		(function () {
			var input = document.createElement("input"),
				select = document.createElement("select"),
				opt = select.appendChild(document.createElement("option"));

			input.type = "checkbox";

			// Support: Android <=4.3 only
			// Default value for a checkbox should be "on"
			support.checkOn = input.value !== "";

			// Support: IE <=11 only
			// Must access selectedIndex to make default options select
			support.optSelected = opt.selected;

			// Support: IE <=11 only
			// An input loses its value after becoming a radio
			input = document.createElement("input");
			input.value = "t";
			input.type = "radio";
			support.radioValue = input.value === "t";
		})();


		var boolHook,
			attrHandle = jQuery.expr.attrHandle;

		jQuery.fn.extend({
			attr: function (name, value) {
				return access(this, jQuery.attr, name, value, arguments.length > 1);
			},

			removeAttr: function (name) {
				return this.each(function () {
					jQuery.removeAttr(this, name);
				});
			}
		});

		jQuery.extend({
			attr: function (elem, name, value) {
				var ret, hooks,
					nType = elem.nodeType;

				// Don't get/set attributes on text, comment and attribute nodes
				if (nType === 3 || nType === 8 || nType === 2) {
					return;
				}

				// Fallback to prop when attributes are not supported
				if (typeof elem.getAttribute === "undefined") {
					return jQuery.prop(elem, name, value);
				}

				// Attribute hooks are determined by the lowercase version
				// Grab necessary hook if one is defined
				if (nType !== 1 || !jQuery.isXMLDoc(elem)) {
					hooks = jQuery.attrHooks[name.toLowerCase()] ||
						(jQuery.expr.match.bool.test(name) ? boolHook : undefined);
				}

				if (value !== undefined) {
					if (value === null) {
						jQuery.removeAttr(elem, name);
						return;
					}

					if (hooks && "set" in hooks &&
						(ret = hooks.set(elem, value, name)) !== undefined) {
						return ret;
					}

					elem.setAttribute(name, value + "");
					return value;
				}

				if (hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null) {
					return ret;
				}

				ret = jQuery.find.attr(elem, name);

				// Non-existent attributes return null, we normalize to undefined
				return ret == null ? undefined : ret;
			},

			attrHooks: {
				type: {
					set: function (elem, value) {
						if (!support.radioValue && value === "radio" &&
							nodeName(elem, "input")) {
							var val = elem.value;
							elem.setAttribute("type", value);
							if (val) {
								elem.value = val;
							}
							return value;
						}
					}
				}
			},

			removeAttr: function (elem, value) {
				var name,
					i = 0,

					// Attribute names can contain non-HTML whitespace characters
					// https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
					attrNames = value && value.match(rnothtmlwhite);

				if (attrNames && elem.nodeType === 1) {
					while ((name = attrNames[i++])) {
						elem.removeAttribute(name);
					}
				}
			}
		});

		// Hooks for boolean attributes
		boolHook = {
			set: function (elem, value, name) {
				if (value === false) {

					// Remove boolean attributes when set to false
					jQuery.removeAttr(elem, name);
				} else {
					elem.setAttribute(name, name);
				}
				return name;
			}
		};

		jQuery.each(jQuery.expr.match.bool.source.match(/\w+/g), function (i, name) {
			var getter = attrHandle[name] || jQuery.find.attr;

			attrHandle[name] = function (elem, name, isXML) {
				var ret, handle,
					lowercaseName = name.toLowerCase();

				if (!isXML) {

					// Avoid an infinite loop by temporarily removing this function from the getter
					handle = attrHandle[lowercaseName];
					attrHandle[lowercaseName] = ret;
					ret = getter(elem, name, isXML) != null ?
						lowercaseName :
						null;
					attrHandle[lowercaseName] = handle;
				}
				return ret;
			};
		});




		var rfocusable = /^(?:input|select|textarea|button)$/i,
			rclickable = /^(?:a|area)$/i;

		jQuery.fn.extend({
			prop: function (name, value) {
				return access(this, jQuery.prop, name, value, arguments.length > 1);
			},

			removeProp: function (name) {
				return this.each(function () {
					delete this[jQuery.propFix[name] || name];
				});
			}
		});

		jQuery.extend({
			prop: function (elem, name, value) {
				var ret, hooks,
					nType = elem.nodeType;

				// Don't get/set properties on text, comment and attribute nodes
				if (nType === 3 || nType === 8 || nType === 2) {
					return;
				}

				if (nType !== 1 || !jQuery.isXMLDoc(elem)) {

					// Fix name and attach hooks
					name = jQuery.propFix[name] || name;
					hooks = jQuery.propHooks[name];
				}

				if (value !== undefined) {
					if (hooks && "set" in hooks &&
						(ret = hooks.set(elem, value, name)) !== undefined) {
						return ret;
					}

					return (elem[name] = value);
				}

				if (hooks && "get" in hooks && (ret = hooks.get(elem, name)) !== null) {
					return ret;
				}

				return elem[name];
			},

			propHooks: {
				tabIndex: {
					get: function (elem) {

						// Support: IE <=9 - 11 only
						// elem.tabIndex doesn't always return the
						// correct value when it hasn't been explicitly set
						// https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
						// Use proper attribute retrieval(#12072)
						var tabindex = jQuery.find.attr(elem, "tabindex");

						if (tabindex) {
							return parseInt(tabindex, 10);
						}

						if (
							rfocusable.test(elem.nodeName) ||
							rclickable.test(elem.nodeName) &&
							elem.href
						) {
							return 0;
						}

						return -1;
					}
				}
			},

			propFix: {
				"for": "htmlFor",
				"class": "className"
			}
		});

		// Support: IE <=11 only
		// Accessing the selectedIndex property
		// forces the browser to respect setting selected
		// on the option
		// The getter ensures a default option is selected
		// when in an optgroup
		// eslint rule "no-unused-expressions" is disabled for this code
		// since it considers such accessions noop
		if (!support.optSelected) {
			jQuery.propHooks.selected = {
				get: function (elem) {

					/* eslint no-unused-expressions: "off" */

					var parent = elem.parentNode;
					if (parent && parent.parentNode) {
						parent.parentNode.selectedIndex;
					}
					return null;
				},
				set: function (elem) {

					/* eslint no-unused-expressions: "off" */

					var parent = elem.parentNode;
					if (parent) {
						parent.selectedIndex;

						if (parent.parentNode) {
							parent.parentNode.selectedIndex;
						}
					}
				}
			};
		}

		jQuery.each([
			"tabIndex",
			"readOnly",
			"maxLength",
			"cellSpacing",
			"cellPadding",
			"rowSpan",
			"colSpan",
			"useMap",
			"frameBorder",
			"contentEditable"
		], function () {
			jQuery.propFix[this.toLowerCase()] = this;
		});




		// Strip and collapse whitespace according to HTML spec
		// https://html.spec.whatwg.org/multipage/infrastructure.html#strip-and-collapse-whitespace
		function stripAndCollapse(value) {
			var tokens = value.match(rnothtmlwhite) || [];
			return tokens.join(" ");
		}


		function getClass(elem) {
			return elem.getAttribute && elem.getAttribute("class") || "";
		}

		jQuery.fn.extend({
			addClass: function (value) {
				var classes, elem, cur, curValue, clazz, j, finalValue,
					i = 0;

				if (jQuery.isFunction(value)) {
					return this.each(function (j) {
						jQuery(this).addClass(value.call(this, j, getClass(this)));
					});
				}

				if (typeof value === "string" && value) {
					classes = value.match(rnothtmlwhite) || [];

					while ((elem = this[i++])) {
						curValue = getClass(elem);
						cur = elem.nodeType === 1 && (" " + stripAndCollapse(curValue) + " ");

						if (cur) {
							j = 0;
							while ((clazz = classes[j++])) {
								if (cur.indexOf(" " + clazz + " ") < 0) {
									cur += clazz + " ";
								}
							}

							// Only assign if different to avoid unneeded rendering.
							finalValue = stripAndCollapse(cur);
							if (curValue !== finalValue) {
								elem.setAttribute("class", finalValue);
							}
						}
					}
				}

				return this;
			},

			removeClass: function (value) {
				var classes, elem, cur, curValue, clazz, j, finalValue,
					i = 0;

				if (jQuery.isFunction(value)) {
					return this.each(function (j) {
						jQuery(this).removeClass(value.call(this, j, getClass(this)));
					});
				}

				if (!arguments.length) {
					return this.attr("class", "");
				}

				if (typeof value === "string" && value) {
					classes = value.match(rnothtmlwhite) || [];

					while ((elem = this[i++])) {
						curValue = getClass(elem);

						// This expression is here for better compressibility (see addClass)
						cur = elem.nodeType === 1 && (" " + stripAndCollapse(curValue) + " ");

						if (cur) {
							j = 0;
							while ((clazz = classes[j++])) {

								// Remove *all* instances
								while (cur.indexOf(" " + clazz + " ") > -1) {
									cur = cur.replace(" " + clazz + " ", " ");
								}
							}

							// Only assign if different to avoid unneeded rendering.
							finalValue = stripAndCollapse(cur);
							if (curValue !== finalValue) {
								elem.setAttribute("class", finalValue);
							}
						}
					}
				}

				return this;
			},

			toggleClass: function (value, stateVal) {
				var type = typeof value;

				if (typeof stateVal === "boolean" && type === "string") {
					return stateVal ? this.addClass(value) : this.removeClass(value);
				}

				if (jQuery.isFunction(value)) {
					return this.each(function (i) {
						jQuery(this).toggleClass(
							value.call(this, i, getClass(this), stateVal),
							stateVal
						);
					});
				}

				return this.each(function () {
					var className, i, self, classNames;

					if (type === "string") {

						// Toggle individual class names
						i = 0;
						self = jQuery(this);
						classNames = value.match(rnothtmlwhite) || [];

						while ((className = classNames[i++])) {

							// Check each className given, space separated list
							if (self.hasClass(className)) {
								self.removeClass(className);
							} else {
								self.addClass(className);
							}
						}

						// Toggle whole class name
					} else if (value === undefined || type === "boolean") {
						className = getClass(this);
						if (className) {

							// Store className if set
							dataPriv.set(this, "__className__", className);
						}

						// If the element has a class name or if we're passed `false`,
						// then remove the whole classname (if there was one, the above saved it).
						// Otherwise bring back whatever was previously saved (if anything),
						// falling back to the empty string if nothing was stored.
						if (this.setAttribute) {
							this.setAttribute("class",
								className || value === false ?
									"" :
									dataPriv.get(this, "__className__") || ""
							);
						}
					}
				});
			},

			hasClass: function (selector) {
				var className, elem,
					i = 0;

				className = " " + selector + " ";
				while ((elem = this[i++])) {
					if (elem.nodeType === 1 &&
						(" " + stripAndCollapse(getClass(elem)) + " ").indexOf(className) > -1) {
						return true;
					}
				}

				return false;
			}
		});




		var rreturn = /\r/g;

		jQuery.fn.extend({
			val: function (value) {
				var hooks, ret, isFunction,
					elem = this[0];

				if (!arguments.length) {
					if (elem) {
						hooks = jQuery.valHooks[elem.type] ||
							jQuery.valHooks[elem.nodeName.toLowerCase()];

						if (hooks &&
							"get" in hooks &&
							(ret = hooks.get(elem, "value")) !== undefined
						) {
							return ret;
						}

						ret = elem.value;

						// Handle most common string cases
						if (typeof ret === "string") {
							return ret.replace(rreturn, "");
						}

						// Handle cases where value is null/undef or number
						return ret == null ? "" : ret;
					}

					return;
				}

				isFunction = jQuery.isFunction(value);

				return this.each(function (i) {
					var val;

					if (this.nodeType !== 1) {
						return;
					}

					if (isFunction) {
						val = value.call(this, i, jQuery(this).val());
					} else {
						val = value;
					}

					// Treat null/undefined as ""; convert numbers to string
					if (val == null) {
						val = "";

					} else if (typeof val === "number") {
						val += "";

					} else if (Array.isArray(val)) {
						val = jQuery.map(val, function (value) {
							return value == null ? "" : value + "";
						});
					}

					hooks = jQuery.valHooks[this.type] || jQuery.valHooks[this.nodeName.toLowerCase()];

					// If set returns undefined, fall back to normal setting
					if (!hooks || !("set" in hooks) || hooks.set(this, val, "value") === undefined) {
						this.value = val;
					}
				});
			}
		});

		jQuery.extend({
			valHooks: {
				option: {
					get: function (elem) {

						var val = jQuery.find.attr(elem, "value");
						return val != null ?
							val :

							// Support: IE <=10 - 11 only
							// option.text throws exceptions (#14686, #14858)
							// Strip and collapse whitespace
							// https://html.spec.whatwg.org/#strip-and-collapse-whitespace
							stripAndCollapse(jQuery.text(elem));
					}
				},
				select: {
					get: function (elem) {
						var value, option, i,
							options = elem.options,
							index = elem.selectedIndex,
							one = elem.type === "select-one",
							values = one ? null : [],
							max = one ? index + 1 : options.length;

						if (index < 0) {
							i = max;

						} else {
							i = one ? index : 0;
						}

						// Loop through all the selected options
						for (; i < max; i++) {
							option = options[i];

							// Support: IE <=9 only
							// IE8-9 doesn't update selected after form reset (#2551)
							if ((option.selected || i === index) &&

								// Don't return options that are disabled or in a disabled optgroup
								!option.disabled &&
								(!option.parentNode.disabled ||
									!nodeName(option.parentNode, "optgroup"))) {

								// Get the specific value for the option
								value = jQuery(option).val();

								// We don't need an array for one selects
								if (one) {
									return value;
								}

								// Multi-Selects return an array
								values.push(value);
							}
						}

						return values;
					},

					set: function (elem, value) {
						var optionSet, option,
							options = elem.options,
							values = jQuery.makeArray(value),
							i = options.length;

						while (i--) {
							option = options[i];

							/* eslint-disable no-cond-assign */

							if (option.selected =
								jQuery.inArray(jQuery.valHooks.option.get(option), values) > -1
							) {
								optionSet = true;
							}

							/* eslint-enable no-cond-assign */
						}

						// Force browsers to behave consistently when non-matching value is set
						if (!optionSet) {
							elem.selectedIndex = -1;
						}
						return values;
					}
				}
			}
		});

		// Radios and checkboxes getter/setter
		jQuery.each(["radio", "checkbox"], function () {
			jQuery.valHooks[this] = {
				set: function (elem, value) {
					if (Array.isArray(value)) {
						return (elem.checked = jQuery.inArray(jQuery(elem).val(), value) > -1);
					}
				}
			};
			if (!support.checkOn) {
				jQuery.valHooks[this].get = function (elem) {
					return elem.getAttribute("value") === null ? "on" : elem.value;
				};
			}
		});




		// Return jQuery for attributes-only inclusion


		var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/;

		jQuery.extend(jQuery.event, {

			trigger: function (event, data, elem, onlyHandlers) {

				var i, cur, tmp, bubbleType, ontype, handle, special,
					eventPath = [elem || document],
					type = hasOwn.call(event, "type") ? event.type : event,
					namespaces = hasOwn.call(event, "namespace") ? event.namespace.split(".") : [];

				cur = tmp = elem = elem || document;

				// Don't do events on text and comment nodes
				if (elem.nodeType === 3 || elem.nodeType === 8) {
					return;
				}

				// focus/blur morphs to focusin/out; ensure we're not firing them right now
				if (rfocusMorph.test(type + jQuery.event.triggered)) {
					return;
				}

				if (type.indexOf(".") > -1) {

					// Namespaced trigger; create a regexp to match event type in handle()
					namespaces = type.split(".");
					type = namespaces.shift();
					namespaces.sort();
				}
				ontype = type.indexOf(":") < 0 && "on" + type;

				// Caller can pass in a jQuery.Event object, Object, or just an event type string
				event = event[jQuery.expando] ?
					event :
					new jQuery.Event(type, typeof event === "object" && event);

				// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
				event.isTrigger = onlyHandlers ? 2 : 3;
				event.namespace = namespaces.join(".");
				event.rnamespace = event.namespace ?
					new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") :
					null;

				// Clean up the event in case it is being reused
				event.result = undefined;
				if (!event.target) {
					event.target = elem;
				}

				// Clone any incoming data and prepend the event, creating the handler arg list
				data = data == null ?
					[event] :
					jQuery.makeArray(data, [event]);

				// Allow special events to draw outside the lines
				special = jQuery.event.special[type] || {};
				if (!onlyHandlers && special.trigger && special.trigger.apply(elem, data) === false) {
					return;
				}

				// Determine event propagation path in advance, per W3C events spec (#9951)
				// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
				if (!onlyHandlers && !special.noBubble && !jQuery.isWindow(elem)) {

					bubbleType = special.delegateType || type;
					if (!rfocusMorph.test(bubbleType + type)) {
						cur = cur.parentNode;
					}
					for (; cur; cur = cur.parentNode) {
						eventPath.push(cur);
						tmp = cur;
					}

					// Only add window if we got to document (e.g., not plain obj or detached DOM)
					if (tmp === (elem.ownerDocument || document)) {
						eventPath.push(tmp.defaultView || tmp.parentWindow || window);
					}
				}

				// Fire handlers on the event path
				i = 0;
				while ((cur = eventPath[i++]) && !event.isPropagationStopped()) {

					event.type = i > 1 ?
						bubbleType :
						special.bindType || type;

					// jQuery handler
					handle = (dataPriv.get(cur, "events") || {})[event.type] &&
						dataPriv.get(cur, "handle");
					if (handle) {
						handle.apply(cur, data);
					}

					// Native handler
					handle = ontype && cur[ontype];
					if (handle && handle.apply && acceptData(cur)) {
						event.result = handle.apply(cur, data);
						if (event.result === false) {
							event.preventDefault();
						}
					}
				}
				event.type = type;

				// If nobody prevented the default action, do it now
				if (!onlyHandlers && !event.isDefaultPrevented()) {

					if ((!special._default ||
						special._default.apply(eventPath.pop(), data) === false) &&
						acceptData(elem)) {

						// Call a native DOM method on the target with the same name as the event.
						// Don't do default actions on window, that's where global variables be (#6170)
						if (ontype && jQuery.isFunction(elem[type]) && !jQuery.isWindow(elem)) {

							// Don't re-trigger an onFOO event when we call its FOO() method
							tmp = elem[ontype];

							if (tmp) {
								elem[ontype] = null;
							}

							// Prevent re-triggering of the same event, since we already bubbled it above
							jQuery.event.triggered = type;
							elem[type]();
							jQuery.event.triggered = undefined;

							if (tmp) {
								elem[ontype] = tmp;
							}
						}
					}
				}

				return event.result;
			},

			// Piggyback on a donor event to simulate a different one
			// Used only for `focus(in | out)` events
			simulate: function (type, elem, event) {
				var e = jQuery.extend(
					new jQuery.Event(),
					event,
					{
						type: type,
						isSimulated: true
					}
				);

				jQuery.event.trigger(e, null, elem);
			}

		});

		jQuery.fn.extend({

			trigger: function (type, data) {
				return this.each(function () {
					jQuery.event.trigger(type, data, this);
				});
			},
			triggerHandler: function (type, data) {
				var elem = this[0];
				if (elem) {
					return jQuery.event.trigger(type, data, elem, true);
				}
			}
		});


		jQuery.each(("blur focus focusin focusout resize scroll click dblclick " +
			"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
			"change select submit keydown keypress keyup contextmenu").split(" "),
			function (i, name) {

				// Handle event binding
				jQuery.fn[name] = function (data, fn) {
					return arguments.length > 0 ?
						this.on(name, null, data, fn) :
						this.trigger(name);
				};
			});

		jQuery.fn.extend({
			hover: function (fnOver, fnOut) {
				return this.mouseenter(fnOver).mouseleave(fnOut || fnOver);
			}
		});




		support.focusin = "onfocusin" in window;


		// Support: Firefox <=44
		// Firefox doesn't have focus(in | out) events
		// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
		//
		// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
		// focus(in | out) events fire after focus & blur events,
		// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
		// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
		if (!support.focusin) {
			jQuery.each({ focus: "focusin", blur: "focusout" }, function (orig, fix) {

				// Attach a single capturing handler on the document while someone wants focusin/focusout
				var handler = function (event) {
					jQuery.event.simulate(fix, event.target, jQuery.event.fix(event));
				};

				jQuery.event.special[fix] = {
					setup: function () {
						var doc = this.ownerDocument || this,
							attaches = dataPriv.access(doc, fix);

						if (!attaches) {
							doc.addEventListener(orig, handler, true);
						}
						dataPriv.access(doc, fix, (attaches || 0) + 1);
					},
					teardown: function () {
						var doc = this.ownerDocument || this,
							attaches = dataPriv.access(doc, fix) - 1;

						if (!attaches) {
							doc.removeEventListener(orig, handler, true);
							dataPriv.remove(doc, fix);

						} else {
							dataPriv.access(doc, fix, attaches);
						}
					}
				};
			});
		}
		var location = window.location;

		var nonce = jQuery.now();

		var rquery = (/\?/);



		// Cross-browser xml parsing
		jQuery.parseXML = function (data) {
			var xml;
			if (!data || typeof data !== "string") {
				return null;
			}

			// Support: IE 9 - 11 only
			// IE throws on parseFromString with invalid input.
			try {
				xml = (new window.DOMParser()).parseFromString(data, "text/xml");
			} catch (e) {
				xml = undefined;
			}

			if (!xml || xml.getElementsByTagName("parsererror").length) {
				jQuery.error("Invalid XML: " + data);
			}
			return xml;
		};


		var
			rbracket = /\[\]$/,
			rCRLF = /\r?\n/g,
			rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
			rsubmittable = /^(?:input|select|textarea|keygen)/i;

		function buildParams(prefix, obj, traditional, add) {
			var name;

			if (Array.isArray(obj)) {

				// Serialize array item.
				jQuery.each(obj, function (i, v) {
					if (traditional || rbracket.test(prefix)) {

						// Treat each array item as a scalar.
						add(prefix, v);

					} else {

						// Item is non-scalar (array or object), encode its numeric index.
						buildParams(
							prefix + "[" + (typeof v === "object" && v != null ? i : "") + "]",
							v,
							traditional,
							add
						);
					}
				});

			} else if (!traditional && jQuery.type(obj) === "object") {

				// Serialize object item.
				for (name in obj) {
					buildParams(prefix + "[" + name + "]", obj[name], traditional, add);
				}

			} else {

				// Serialize scalar item.
				add(prefix, obj);
			}
		}

		// Serialize an array of form elements or a set of
		// key/values into a query string
		jQuery.param = function (a, traditional) {
			var prefix,
				s = [],
				add = function (key, valueOrFunction) {

					// If value is a function, invoke it and use its return value
					var value = jQuery.isFunction(valueOrFunction) ?
						valueOrFunction() :
						valueOrFunction;

					s[s.length] = encodeURIComponent(key) + "=" +
						encodeURIComponent(value == null ? "" : value);
				};

			// If an array was passed in, assume that it is an array of form elements.
			if (Array.isArray(a) || (a.jquery && !jQuery.isPlainObject(a))) {

				// Serialize the form elements
				jQuery.each(a, function () {
					add(this.name, this.value);
				});

			} else {

				// If traditional, encode the "old" way (the way 1.3.2 or older
				// did it), otherwise encode params recursively.
				for (prefix in a) {
					buildParams(prefix, a[prefix], traditional, add);
				}
			}

			// Return the resulting serialization
			return s.join("&");
		};

		jQuery.fn.extend({
			serialize: function () {
				return jQuery.param(this.serializeArray());
			},
			serializeArray: function () {
				return this.map(function () {

					// Can add propHook for "elements" to filter or add form elements
					var elements = jQuery.prop(this, "elements");
					return elements ? jQuery.makeArray(elements) : this;
				})
					.filter(function () {
						var type = this.type;

						// Use .is( ":disabled" ) so that fieldset[disabled] works
						return this.name && !jQuery(this).is(":disabled") &&
							rsubmittable.test(this.nodeName) && !rsubmitterTypes.test(type) &&
							(this.checked || !rcheckableType.test(type));
					})
					.map(function (i, elem) {
						var val = jQuery(this).val();

						if (val == null) {
							return null;
						}

						if (Array.isArray(val)) {
							return jQuery.map(val, function (val) {
								return { name: elem.name, value: val.replace(rCRLF, "\r\n") };
							});
						}

						return { name: elem.name, value: val.replace(rCRLF, "\r\n") };
					}).get();
			}
		});


		var
			r20 = /%20/g,
			rhash = /#.*$/,
			rantiCache = /([?&])_=[^&]*/,
			rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,

			// #7653, #8125, #8152: local protocol detection
			rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
			rnoContent = /^(?:GET|HEAD)$/,
			rprotocol = /^\/\//,

			/* Prefilters
			 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
			 * 2) These are called:
			 *    - BEFORE asking for a transport
			 *    - AFTER param serialization (s.data is a string if s.processData is true)
			 * 3) key is the dataType
			 * 4) the catchall symbol "*" can be used
			 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
			 */
			prefilters = {},

			/* Transports bindings
			 * 1) key is the dataType
			 * 2) the catchall symbol "*" can be used
			 * 3) selection will start with transport dataType and THEN go to "*" if needed
			 */
			transports = {},

			// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
			allTypes = "*/".concat("*"),

			// Anchor tag for parsing the document origin
			originAnchor = document.createElement("a");
		originAnchor.href = location.href;

		// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
		function addToPrefiltersOrTransports(structure) {

			// dataTypeExpression is optional and defaults to "*"
			return function (dataTypeExpression, func) {

				if (typeof dataTypeExpression !== "string") {
					func = dataTypeExpression;
					dataTypeExpression = "*";
				}

				var dataType,
					i = 0,
					dataTypes = dataTypeExpression.toLowerCase().match(rnothtmlwhite) || [];

				if (jQuery.isFunction(func)) {

					// For each dataType in the dataTypeExpression
					while ((dataType = dataTypes[i++])) {

						// Prepend if requested
						if (dataType[0] === "+") {
							dataType = dataType.slice(1) || "*";
							(structure[dataType] = structure[dataType] || []).unshift(func);

							// Otherwise append
						} else {
							(structure[dataType] = structure[dataType] || []).push(func);
						}
					}
				}
			};
		}

		// Base inspection function for prefilters and transports
		function inspectPrefiltersOrTransports(structure, options, originalOptions, jqXHR) {

			var inspected = {},
				seekingTransport = (structure === transports);

			function inspect(dataType) {
				var selected;
				inspected[dataType] = true;
				jQuery.each(structure[dataType] || [], function (_, prefilterOrFactory) {
					var dataTypeOrTransport = prefilterOrFactory(options, originalOptions, jqXHR);
					if (typeof dataTypeOrTransport === "string" &&
						!seekingTransport && !inspected[dataTypeOrTransport]) {

						options.dataTypes.unshift(dataTypeOrTransport);
						inspect(dataTypeOrTransport);
						return false;
					} else if (seekingTransport) {
						return !(selected = dataTypeOrTransport);
					}
				});
				return selected;
			}

			return inspect(options.dataTypes[0]) || !inspected["*"] && inspect("*");
		}

		// A special extend for ajax options
		// that takes "flat" options (not to be deep extended)
		// Fixes #9887
		function ajaxExtend(target, src) {
			var key, deep,
				flatOptions = jQuery.ajaxSettings.flatOptions || {};

			for (key in src) {
				if (src[key] !== undefined) {
					(flatOptions[key] ? target : (deep || (deep = {})))[key] = src[key];
				}
			}
			if (deep) {
				jQuery.extend(true, target, deep);
			}

			return target;
		}

		/* Handles responses to an ajax request:
		 * - finds the right dataType (mediates between content-type and expected dataType)
		 * - returns the corresponding response
		 */
		function ajaxHandleResponses(s, jqXHR, responses) {

			var ct, type, finalDataType, firstDataType,
				contents = s.contents,
				dataTypes = s.dataTypes;

			// Remove auto dataType and get content-type in the process
			while (dataTypes[0] === "*") {
				dataTypes.shift();
				if (ct === undefined) {
					ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
				}
			}

			// Check if we're dealing with a known content-type
			if (ct) {
				for (type in contents) {
					if (contents[type] && contents[type].test(ct)) {
						dataTypes.unshift(type);
						break;
					}
				}
			}

			// Check to see if we have a response for the expected dataType
			if (dataTypes[0] in responses) {
				finalDataType = dataTypes[0];
			} else {

				// Try convertible dataTypes
				for (type in responses) {
					if (!dataTypes[0] || s.converters[type + " " + dataTypes[0]]) {
						finalDataType = type;
						break;
					}
					if (!firstDataType) {
						firstDataType = type;
					}
				}

				// Or just use first one
				finalDataType = finalDataType || firstDataType;
			}

			// If we found a dataType
			// We add the dataType to the list if needed
			// and return the corresponding response
			if (finalDataType) {
				if (finalDataType !== dataTypes[0]) {
					dataTypes.unshift(finalDataType);
				}
				return responses[finalDataType];
			}
		}

		/* Chain conversions given the request and the original response
		 * Also sets the responseXXX fields on the jqXHR instance
		 */
		function ajaxConvert(s, response, jqXHR, isSuccess) {
			var conv2, current, conv, tmp, prev,
				converters = {},

				// Work with a copy of dataTypes in case we need to modify it for conversion
				dataTypes = s.dataTypes.slice();

			// Create converters map with lowercased keys
			if (dataTypes[1]) {
				for (conv in s.converters) {
					converters[conv.toLowerCase()] = s.converters[conv];
				}
			}

			current = dataTypes.shift();

			// Convert to each sequential dataType
			while (current) {

				if (s.responseFields[current]) {
					jqXHR[s.responseFields[current]] = response;
				}

				// Apply the dataFilter if provided
				if (!prev && isSuccess && s.dataFilter) {
					response = s.dataFilter(response, s.dataType);
				}

				prev = current;
				current = dataTypes.shift();

				if (current) {

					// There's only work to do if current dataType is non-auto
					if (current === "*") {

						current = prev;

						// Convert response if prev dataType is non-auto and differs from current
					} else if (prev !== "*" && prev !== current) {

						// Seek a direct converter
						conv = converters[prev + " " + current] || converters["* " + current];

						// If none found, seek a pair
						if (!conv) {
							for (conv2 in converters) {

								// If conv2 outputs current
								tmp = conv2.split(" ");
								if (tmp[1] === current) {

									// If prev can be converted to accepted input
									conv = converters[prev + " " + tmp[0]] ||
										converters["* " + tmp[0]];
									if (conv) {

										// Condense equivalence converters
										if (conv === true) {
											conv = converters[conv2];

											// Otherwise, insert the intermediate dataType
										} else if (converters[conv2] !== true) {
											current = tmp[0];
											dataTypes.unshift(tmp[1]);
										}
										break;
									}
								}
							}
						}

						// Apply converter (if not an equivalence)
						if (conv !== true) {

							// Unless errors are allowed to bubble, catch and return them
							if (conv && s.throws) {
								response = conv(response);
							} else {
								try {
									response = conv(response);
								} catch (e) {
									return {
										state: "parsererror",
										error: conv ? e : "No conversion from " + prev + " to " + current
									};
								}
							}
						}
					}
				}
			}

			return { state: "success", data: response };
		}

		jQuery.extend({

			// Counter for holding the number of active queries
			active: 0,

			// Last-Modified header cache for next request
			lastModified: {},
			etag: {},

			ajaxSettings: {
				url: location.href,
				type: "GET",
				isLocal: rlocalProtocol.test(location.protocol),
				global: true,
				processData: true,
				async: true,
				contentType: "application/x-www-form-urlencoded; charset=UTF-8",

				/*
				timeout: 0,
				data: null,
				dataType: null,
				username: null,
				password: null,
				cache: null,
				throws: false,
				traditional: false,
				headers: {},
				*/

				accepts: {
					"*": allTypes,
					text: "text/plain",
					html: "text/html",
					xml: "application/xml, text/xml",
					json: "application/json, text/javascript"
				},

				contents: {
					xml: /\bxml\b/,
					html: /\bhtml/,
					json: /\bjson\b/
				},

				responseFields: {
					xml: "responseXML",
					text: "responseText",
					json: "responseJSON"
				},

				// Data converters
				// Keys separate source (or catchall "*") and destination types with a single space
				converters: {

					// Convert anything to text
					"* text": String,

					// Text to html (true = no transformation)
					"text html": true,

					// Evaluate text as a json expression
					"text json": JSON.parse,

					// Parse text as xml
					"text xml": jQuery.parseXML
				},

				// For options that shouldn't be deep extended:
				// you can add your own custom options here if
				// and when you create one that shouldn't be
				// deep extended (see ajaxExtend)
				flatOptions: {
					url: true,
					context: true
				}
			},

			// Creates a full fledged settings object into target
			// with both ajaxSettings and settings fields.
			// If target is omitted, writes into ajaxSettings.
			ajaxSetup: function (target, settings) {
				return settings ?

					// Building a settings object
					ajaxExtend(ajaxExtend(target, jQuery.ajaxSettings), settings) :

					// Extending ajaxSettings
					ajaxExtend(jQuery.ajaxSettings, target);
			},

			ajaxPrefilter: addToPrefiltersOrTransports(prefilters),
			ajaxTransport: addToPrefiltersOrTransports(transports),

			// Main method
			ajax: function (url, options) {

				// If url is an object, simulate pre-1.5 signature
				if (typeof url === "object") {
					options = url;
					url = undefined;
				}

				// Force options to be an object
				options = options || {};

				var transport,

					// URL without anti-cache param
					cacheURL,

					// Response headers
					responseHeadersString,
					responseHeaders,

					// timeout handle
					timeoutTimer,

					// Url cleanup var
					urlAnchor,

					// Request state (becomes false upon send and true upon completion)
					completed,

					// To know if global events are to be dispatched
					fireGlobals,

					// Loop variable
					i,

					// uncached part of the url
					uncached,

					// Create the final options object
					s = jQuery.ajaxSetup({}, options),

					// Callbacks context
					callbackContext = s.context || s,

					// Context for global events is callbackContext if it is a DOM node or jQuery collection
					globalEventContext = s.context &&
						(callbackContext.nodeType || callbackContext.jquery) ?
						jQuery(callbackContext) :
						jQuery.event,

					// Deferreds
					deferred = jQuery.Deferred(),
					completeDeferred = jQuery.Callbacks("once memory"),

					// Status-dependent callbacks
					statusCode = s.statusCode || {},

					// Headers (they are sent all at once)
					requestHeaders = {},
					requestHeadersNames = {},

					// Default abort message
					strAbort = "canceled",

					// Fake xhr
					jqXHR = {
						readyState: 0,

						// Builds headers hashtable if needed
						getResponseHeader: function (key) {
							var match;
							if (completed) {
								if (!responseHeaders) {
									responseHeaders = {};
									while ((match = rheaders.exec(responseHeadersString))) {
										responseHeaders[match[1].toLowerCase()] = match[2];
									}
								}
								match = responseHeaders[key.toLowerCase()];
							}
							return match == null ? null : match;
						},

						// Raw string
						getAllResponseHeaders: function () {
							return completed ? responseHeadersString : null;
						},

						// Caches the header
						setRequestHeader: function (name, value) {
							if (completed == null) {
								name = requestHeadersNames[name.toLowerCase()] =
									requestHeadersNames[name.toLowerCase()] || name;
								requestHeaders[name] = value;
							}
							return this;
						},

						// Overrides response content-type header
						overrideMimeType: function (type) {
							if (completed == null) {
								s.mimeType = type;
							}
							return this;
						},

						// Status-dependent callbacks
						statusCode: function (map) {
							var code;
							if (map) {
								if (completed) {

									// Execute the appropriate callbacks
									jqXHR.always(map[jqXHR.status]);
								} else {

									// Lazy-add the new callbacks in a way that preserves old ones
									for (code in map) {
										statusCode[code] = [statusCode[code], map[code]];
									}
								}
							}
							return this;
						},

						// Cancel the request
						abort: function (statusText) {
							var finalText = statusText || strAbort;
							if (transport) {
								transport.abort(finalText);
							}
							done(0, finalText);
							return this;
						}
					};

				// Attach deferreds
				deferred.promise(jqXHR);

				// Add protocol if not provided (prefilters might expect it)
				// Handle falsy url in the settings object (#10093: consistency with old signature)
				// We also use the url parameter if available
				s.url = ((url || s.url || location.href) + "")
					.replace(rprotocol, location.protocol + "//");

				// Alias method option to type as per ticket #12004
				s.type = options.method || options.type || s.method || s.type;

				// Extract dataTypes list
				s.dataTypes = (s.dataType || "*").toLowerCase().match(rnothtmlwhite) || [""];

				// A cross-domain request is in order when the origin doesn't match the current origin.
				if (s.crossDomain == null) {
					urlAnchor = document.createElement("a");

					// Support: IE <=8 - 11, Edge 12 - 13
					// IE throws exception on accessing the href property if url is malformed,
					// e.g. http://example.com:80x/
					try {
						urlAnchor.href = s.url;

						// Support: IE <=8 - 11 only
						// Anchor's host property isn't correctly set when s.url is relative
						urlAnchor.href = urlAnchor.href;
						s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
							urlAnchor.protocol + "//" + urlAnchor.host;
					} catch (e) {

						// If there is an error parsing the URL, assume it is crossDomain,
						// it can be rejected by the transport if it is invalid
						s.crossDomain = true;
					}
				}

				// Convert data if not already a string
				if (s.data && s.processData && typeof s.data !== "string") {
					s.data = jQuery.param(s.data, s.traditional);
				}

				// Apply prefilters
				inspectPrefiltersOrTransports(prefilters, s, options, jqXHR);

				// If request was aborted inside a prefilter, stop there
				if (completed) {
					return jqXHR;
				}

				// We can fire global events as of now if asked to
				// Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
				fireGlobals = jQuery.event && s.global;

				// Watch for a new set of requests
				if (fireGlobals && jQuery.active++ === 0) {
					jQuery.event.trigger("ajaxStart");
				}

				// Uppercase the type
				s.type = s.type.toUpperCase();

				// Determine if request has content
				s.hasContent = !rnoContent.test(s.type);

				// Save the URL in case we're toying with the If-Modified-Since
				// and/or If-None-Match header later on
				// Remove hash to simplify url manipulation
				cacheURL = s.url.replace(rhash, "");

				// More options handling for requests with no content
				if (!s.hasContent) {

					// Remember the hash so we can put it back
					uncached = s.url.slice(cacheURL.length);

					// If data is available, append data to url
					if (s.data) {
						cacheURL += (rquery.test(cacheURL) ? "&" : "?") + s.data;

						// #9682: remove data so that it's not used in an eventual retry
						delete s.data;
					}

					// Add or update anti-cache param if needed
					if (s.cache === false) {
						cacheURL = cacheURL.replace(rantiCache, "$1");
						uncached = (rquery.test(cacheURL) ? "&" : "?") + "_=" + (nonce++) + uncached;
					}

					// Put hash and anti-cache on the URL that will be requested (gh-1732)
					s.url = cacheURL + uncached;

					// Change '%20' to '+' if this is encoded form body content (gh-2658)
				} else if (s.data && s.processData &&
					(s.contentType || "").indexOf("application/x-www-form-urlencoded") === 0) {
					s.data = s.data.replace(r20, "+");
				}

				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
				if (s.ifModified) {
					if (jQuery.lastModified[cacheURL]) {
						jqXHR.setRequestHeader("If-Modified-Since", jQuery.lastModified[cacheURL]);
					}
					if (jQuery.etag[cacheURL]) {
						jqXHR.setRequestHeader("If-None-Match", jQuery.etag[cacheURL]);
					}
				}

				// Set the correct header, if data is being sent
				if (s.data && s.hasContent && s.contentType !== false || options.contentType) {
					jqXHR.setRequestHeader("Content-Type", s.contentType);
				}

				// Set the Accepts header for the server, depending on the dataType
				jqXHR.setRequestHeader(
					"Accept",
					s.dataTypes[0] && s.accepts[s.dataTypes[0]] ?
						s.accepts[s.dataTypes[0]] +
						(s.dataTypes[0] !== "*" ? ", " + allTypes + "; q=0.01" : "") :
						s.accepts["*"]
				);

				// Check for headers option
				for (i in s.headers) {
					jqXHR.setRequestHeader(i, s.headers[i]);
				}

				// Allow custom headers/mimetypes and early abort
				if (s.beforeSend &&
					(s.beforeSend.call(callbackContext, jqXHR, s) === false || completed)) {

					// Abort if not done already and return
					return jqXHR.abort();
				}

				// Aborting is no longer a cancellation
				strAbort = "abort";

				// Install callbacks on deferreds
				completeDeferred.add(s.complete);
				jqXHR.done(s.success);
				jqXHR.fail(s.error);

				// Get transport
				transport = inspectPrefiltersOrTransports(transports, s, options, jqXHR);

				// If no transport, we auto-abort
				if (!transport) {
					done(-1, "No Transport");
				} else {
					jqXHR.readyState = 1;

					// Send global event
					if (fireGlobals) {
						globalEventContext.trigger("ajaxSend", [jqXHR, s]);
					}

					// If request was aborted inside ajaxSend, stop there
					if (completed) {
						return jqXHR;
					}

					// Timeout
					if (s.async && s.timeout > 0) {
						timeoutTimer = window.setTimeout(function () {
							jqXHR.abort("timeout");
						}, s.timeout);
					}

					try {
						completed = false;
						transport.send(requestHeaders, done);
					} catch (e) {

						// Rethrow post-completion exceptions
						if (completed) {
							throw e;
						}

						// Propagate others as results
						done(-1, e);
					}
				}

				// Callback for when everything is done
				function done(status, nativeStatusText, responses, headers) {
					var isSuccess, success, error, response, modified,
						statusText = nativeStatusText;

					// Ignore repeat invocations
					if (completed) {
						return;
					}

					completed = true;

					// Clear timeout if it exists
					if (timeoutTimer) {
						window.clearTimeout(timeoutTimer);
					}

					// Dereference transport for early garbage collection
					// (no matter how long the jqXHR object will be used)
					transport = undefined;

					// Cache response headers
					responseHeadersString = headers || "";

					// Set readyState
					jqXHR.readyState = status > 0 ? 4 : 0;

					// Determine if successful
					isSuccess = status >= 200 && status < 300 || status === 304;

					// Get response data
					if (responses) {
						response = ajaxHandleResponses(s, jqXHR, responses);
					}

					// Convert no matter what (that way responseXXX fields are always set)
					response = ajaxConvert(s, response, jqXHR, isSuccess);

					// If successful, handle type chaining
					if (isSuccess) {

						// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
						if (s.ifModified) {
							modified = jqXHR.getResponseHeader("Last-Modified");
							if (modified) {
								jQuery.lastModified[cacheURL] = modified;
							}
							modified = jqXHR.getResponseHeader("etag");
							if (modified) {
								jQuery.etag[cacheURL] = modified;
							}
						}

						// if no content
						if (status === 204 || s.type === "HEAD") {
							statusText = "nocontent";

							// if not modified
						} else if (status === 304) {
							statusText = "notmodified";

							// If we have data, let's convert it
						} else {
							statusText = response.state;
							success = response.data;
							error = response.error;
							isSuccess = !error;
						}
					} else {

						// Extract error from statusText and normalize for non-aborts
						error = statusText;
						if (status || !statusText) {
							statusText = "error";
							if (status < 0) {
								status = 0;
							}
						}
					}

					// Set data for the fake xhr object
					jqXHR.status = status;
					jqXHR.statusText = (nativeStatusText || statusText) + "";

					// Success/Error
					if (isSuccess) {
						deferred.resolveWith(callbackContext, [success, statusText, jqXHR]);
					} else {
						deferred.rejectWith(callbackContext, [jqXHR, statusText, error]);
					}

					// Status-dependent callbacks
					jqXHR.statusCode(statusCode);
					statusCode = undefined;

					if (fireGlobals) {
						globalEventContext.trigger(isSuccess ? "ajaxSuccess" : "ajaxError",
							[jqXHR, s, isSuccess ? success : error]);
					}

					// Complete
					completeDeferred.fireWith(callbackContext, [jqXHR, statusText]);

					if (fireGlobals) {
						globalEventContext.trigger("ajaxComplete", [jqXHR, s]);

						// Handle the global AJAX counter
						if (!(--jQuery.active)) {
							jQuery.event.trigger("ajaxStop");
						}
					}
				}

				return jqXHR;
			},

			getJSON: function (url, data, callback) {
				return jQuery.get(url, data, callback, "json");
			},

			getScript: function (url, callback) {
				return jQuery.get(url, undefined, callback, "script");
			}
		});

		jQuery.each(["get", "post"], function (i, method) {
			jQuery[method] = function (url, data, callback, type) {

				// Shift arguments if data argument was omitted
				if (jQuery.isFunction(data)) {
					type = type || callback;
					callback = data;
					data = undefined;
				}

				// The url can be an options object (which then must have .url)
				return jQuery.ajax(jQuery.extend({
					url: url,
					type: method,
					dataType: type,
					data: data,
					success: callback
				}, jQuery.isPlainObject(url) && url));
			};
		});


		jQuery._evalUrl = function (url) {
			return jQuery.ajax({
				url: url,

				// Make this explicit, since user can override this through ajaxSetup (#11264)
				type: "GET",
				dataType: "script",
				cache: true,
				async: false,
				global: false,
				"throws": true
			});
		};


		jQuery.fn.extend({
			wrapAll: function (html) {
				var wrap;

				if (this[0]) {
					if (jQuery.isFunction(html)) {
						html = html.call(this[0]);
					}

					// The elements to wrap the target around
					wrap = jQuery(html, this[0].ownerDocument).eq(0).clone(true);

					if (this[0].parentNode) {
						wrap.insertBefore(this[0]);
					}

					wrap.map(function () {
						var elem = this;

						while (elem.firstElementChild) {
							elem = elem.firstElementChild;
						}

						return elem;
					}).append(this);
				}

				return this;
			},

			wrapInner: function (html) {
				if (jQuery.isFunction(html)) {
					return this.each(function (i) {
						jQuery(this).wrapInner(html.call(this, i));
					});
				}

				return this.each(function () {
					var self = jQuery(this),
						contents = self.contents();

					if (contents.length) {
						contents.wrapAll(html);

					} else {
						self.append(html);
					}
				});
			},

			wrap: function (html) {
				var isFunction = jQuery.isFunction(html);

				return this.each(function (i) {
					jQuery(this).wrapAll(isFunction ? html.call(this, i) : html);
				});
			},

			unwrap: function (selector) {
				this.parent(selector).not("body").each(function () {
					jQuery(this).replaceWith(this.childNodes);
				});
				return this;
			}
		});


		jQuery.expr.pseudos.hidden = function (elem) {
			return !jQuery.expr.pseudos.visible(elem);
		};
		jQuery.expr.pseudos.visible = function (elem) {
			return !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length);
		};




		jQuery.ajaxSettings.xhr = function () {
			try {
				return new window.XMLHttpRequest();
			} catch (e) { }
		};

		var xhrSuccessStatus = {

			// File protocol always yields status code 0, assume 200
			0: 200,

			// Support: IE <=9 only
			// #1450: sometimes IE returns 1223 when it should be 204
			1223: 204
		},
			xhrSupported = jQuery.ajaxSettings.xhr();

		support.cors = !!xhrSupported && ("withCredentials" in xhrSupported);
		support.ajax = xhrSupported = !!xhrSupported;

		jQuery.ajaxTransport(function (options) {
			var callback, errorCallback;

			// Cross domain only allowed if supported through XMLHttpRequest
			if (support.cors || xhrSupported && !options.crossDomain) {
				return {
					send: function (headers, complete) {
						var i,
							xhr = options.xhr();

						xhr.open(
							options.type,
							options.url,
							options.async,
							options.username,
							options.password
						);

						// Apply custom fields if provided
						if (options.xhrFields) {
							for (i in options.xhrFields) {
								xhr[i] = options.xhrFields[i];
							}
						}

						// Override mime type if needed
						if (options.mimeType && xhr.overrideMimeType) {
							xhr.overrideMimeType(options.mimeType);
						}

						// X-Requested-With header
						// For cross-domain requests, seeing as conditions for a preflight are
						// akin to a jigsaw puzzle, we simply never set it to be sure.
						// (it can always be set on a per-request basis or even using ajaxSetup)
						// For same-domain requests, won't change header if already provided.
						if (!options.crossDomain && !headers["X-Requested-With"]) {
							headers["X-Requested-With"] = "XMLHttpRequest";
						}

						// Set headers
						for (i in headers) {
							xhr.setRequestHeader(i, headers[i]);
						}

						// Callback
						callback = function (type) {
							return function () {
								if (callback) {
									callback = errorCallback = xhr.onload =
										xhr.onerror = xhr.onabort = xhr.onreadystatechange = null;

									if (type === "abort") {
										xhr.abort();
									} else if (type === "error") {

										// Support: IE <=9 only
										// On a manual native abort, IE9 throws
										// errors on any property access that is not readyState
										if (typeof xhr.status !== "number") {
											complete(0, "error");
										} else {
											complete(

												// File: protocol always yields status 0; see #8605, #14207
												xhr.status,
												xhr.statusText
											);
										}
									} else {
										complete(
											xhrSuccessStatus[xhr.status] || xhr.status,
											xhr.statusText,

											// Support: IE <=9 only
											// IE9 has no XHR2 but throws on binary (trac-11426)
											// For XHR2 non-text, let the caller handle it (gh-2498)
											(xhr.responseType || "text") !== "text" ||
												typeof xhr.responseText !== "string" ?
												{ binary: xhr.response } :
												{ text: xhr.responseText },
											xhr.getAllResponseHeaders()
										);
									}
								}
							};
						};

						// Listen to events
						xhr.onload = callback();
						errorCallback = xhr.onerror = callback("error");

						// Support: IE 9 only
						// Use onreadystatechange to replace onabort
						// to handle uncaught aborts
						if (xhr.onabort !== undefined) {
							xhr.onabort = errorCallback;
						} else {
							xhr.onreadystatechange = function () {

								// Check readyState before timeout as it changes
								if (xhr.readyState === 4) {

									// Allow onerror to be called first,
									// but that will not handle a native abort
									// Also, save errorCallback to a variable
									// as xhr.onerror cannot be accessed
									window.setTimeout(function () {
										if (callback) {
											errorCallback();
										}
									});
								}
							};
						}

						// Create the abort callback
						callback = callback("abort");

						try {

							// Do send the request (this may raise an exception)
							xhr.send(options.hasContent && options.data || null);
						} catch (e) {

							// #14683: Only rethrow if this hasn't been notified as an error yet
							if (callback) {
								throw e;
							}
						}
					},

					abort: function () {
						if (callback) {
							callback();
						}
					}
				};
			}
		});




		// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
		jQuery.ajaxPrefilter(function (s) {
			if (s.crossDomain) {
				s.contents.script = false;
			}
		});

		// Install script dataType
		jQuery.ajaxSetup({
			accepts: {
				script: "text/javascript, application/javascript, " +
					"application/ecmascript, application/x-ecmascript"
			},
			contents: {
				script: /\b(?:java|ecma)script\b/
			},
			converters: {
				"text script": function (text) {
					jQuery.globalEval(text);
					return text;
				}
			}
		});

		// Handle cache's special case and crossDomain
		jQuery.ajaxPrefilter("script", function (s) {
			if (s.cache === undefined) {
				s.cache = false;
			}
			if (s.crossDomain) {
				s.type = "GET";
			}
		});

		// Bind script tag hack transport
		jQuery.ajaxTransport("script", function (s) {

			// This transport only deals with cross domain requests
			if (s.crossDomain) {
				var script, callback;
				return {
					send: function (_, complete) {
						script = jQuery("<script>").prop({
							charset: s.scriptCharset,
							src: s.url
						}).on(
							"load error",
							callback = function (evt) {
								script.remove();
								callback = null;
								if (evt) {
									complete(evt.type === "error" ? 404 : 200, evt.type);
								}
							}
						);

						// Use native DOM manipulation to avoid our domManip AJAX trickery
						document.head.appendChild(script[0]);
					},
					abort: function () {
						if (callback) {
							callback();
						}
					}
				};
			}
		});




		var oldCallbacks = [],
			rjsonp = /(=)\?(?=&|$)|\?\?/;

		// Default jsonp settings
		jQuery.ajaxSetup({
			jsonp: "callback",
			jsonpCallback: function () {
				var callback = oldCallbacks.pop() || (jQuery.expando + "_" + (nonce++));
				this[callback] = true;
				return callback;
			}
		});

		// Detect, normalize options and install callbacks for jsonp requests
		jQuery.ajaxPrefilter("json jsonp", function (s, originalSettings, jqXHR) {

			var callbackName, overwritten, responseContainer,
				jsonProp = s.jsonp !== false && (rjsonp.test(s.url) ?
					"url" :
					typeof s.data === "string" &&
					(s.contentType || "")
						.indexOf("application/x-www-form-urlencoded") === 0 &&
					rjsonp.test(s.data) && "data"
				);

			// Handle iff the expected data type is "jsonp" or we have a parameter to set
			if (jsonProp || s.dataTypes[0] === "jsonp") {

				// Get callback name, remembering preexisting value associated with it
				callbackName = s.jsonpCallback = jQuery.isFunction(s.jsonpCallback) ?
					s.jsonpCallback() :
					s.jsonpCallback;

				// Insert callback into url or form data
				if (jsonProp) {
					s[jsonProp] = s[jsonProp].replace(rjsonp, "$1" + callbackName);
				} else if (s.jsonp !== false) {
					s.url += (rquery.test(s.url) ? "&" : "?") + s.jsonp + "=" + callbackName;
				}

				// Use data converter to retrieve json after script execution
				s.converters["script json"] = function () {
					if (!responseContainer) {
						jQuery.error(callbackName + " was not called");
					}
					return responseContainer[0];
				};

				// Force json dataType
				s.dataTypes[0] = "json";

				// Install callback
				overwritten = window[callbackName];
				window[callbackName] = function () {
					responseContainer = arguments;
				};

				// Clean-up function (fires after converters)
				jqXHR.always(function () {

					// If previous value didn't exist - remove it
					if (overwritten === undefined) {
						jQuery(window).removeProp(callbackName);

						// Otherwise restore preexisting value
					} else {
						window[callbackName] = overwritten;
					}

					// Save back as free
					if (s[callbackName]) {

						// Make sure that re-using the options doesn't screw things around
						s.jsonpCallback = originalSettings.jsonpCallback;

						// Save the callback name for future use
						oldCallbacks.push(callbackName);
					}

					// Call if it was a function and we have a response
					if (responseContainer && jQuery.isFunction(overwritten)) {
						overwritten(responseContainer[0]);
					}

					responseContainer = overwritten = undefined;
				});

				// Delegate to script
				return "script";
			}
		});




		// Support: Safari 8 only
		// In Safari 8 documents created via document.implementation.createHTMLDocument
		// collapse sibling forms: the second one becomes a child of the first one.
		// Because of that, this security measure has to be disabled in Safari 8.
		// https://bugs.webkit.org/show_bug.cgi?id=137337
		support.createHTMLDocument = (function () {
			var body = document.implementation.createHTMLDocument("").body;
			body.innerHTML = "<form></form><form></form>";
			return body.childNodes.length === 2;
		})();


		// Argument "data" should be string of html
		// context (optional): If specified, the fragment will be created in this context,
		// defaults to document
		// keepScripts (optional): If true, will include scripts passed in the html string
		jQuery.parseHTML = function (data, context, keepScripts) {
			if (typeof data !== "string") {
				return [];
			}
			if (typeof context === "boolean") {
				keepScripts = context;
				context = false;
			}

			var base, parsed, scripts;

			if (!context) {

				// Stop scripts or inline event handlers from being executed immediately
				// by using document.implementation
				if (support.createHTMLDocument) {
					context = document.implementation.createHTMLDocument("");

					// Set the base href for the created document
					// so any parsed elements with URLs
					// are based on the document's URL (gh-2965)
					base = context.createElement("base");
					base.href = document.location.href;
					context.head.appendChild(base);
				} else {
					context = document;
				}
			}

			parsed = rsingleTag.exec(data);
			scripts = !keepScripts && [];

			// Single tag
			if (parsed) {
				return [context.createElement(parsed[1])];
			}

			parsed = buildFragment([data], context, scripts);

			if (scripts && scripts.length) {
				jQuery(scripts).remove();
			}

			return jQuery.merge([], parsed.childNodes);
		};


		/**
		 * Load a url into a page
		 */
		jQuery.fn.load = function (url, params, callback) {
			var selector, type, response,
				self = this,
				off = url.indexOf(" ");

			if (off > -1) {
				selector = stripAndCollapse(url.slice(off));
				url = url.slice(0, off);
			}

			// If it's a function
			if (jQuery.isFunction(params)) {

				// We assume that it's the callback
				callback = params;
				params = undefined;

				// Otherwise, build a param string
			} else if (params && typeof params === "object") {
				type = "POST";
			}

			// If we have elements to modify, make the request
			if (self.length > 0) {
				jQuery.ajax({
					url: url,

					// If "type" variable is undefined, then "GET" method will be used.
					// Make value of this field explicit since
					// user can override it through ajaxSetup method
					type: type || "GET",
					dataType: "html",
					data: params
				}).done(function (responseText) {

					// Save response for use in complete callback
					response = arguments;

					self.html(selector ?

						// If a selector was specified, locate the right elements in a dummy div
						// Exclude scripts to avoid IE 'Permission Denied' errors
						jQuery("<div>").append(jQuery.parseHTML(responseText)).find(selector) :

						// Otherwise use the full result
						responseText);

					// If the request succeeds, this function gets "data", "status", "jqXHR"
					// but they are ignored because response was set above.
					// If it fails, this function gets "jqXHR", "status", "error"
				}).always(callback && function (jqXHR, status) {
					self.each(function () {
						callback.apply(this, response || [jqXHR.responseText, status, jqXHR]);
					});
				});
			}

			return this;
		};




		// Attach a bunch of functions for handling common AJAX events
		jQuery.each([
			"ajaxStart",
			"ajaxStop",
			"ajaxComplete",
			"ajaxError",
			"ajaxSuccess",
			"ajaxSend"
		], function (i, type) {
			jQuery.fn[type] = function (fn) {
				return this.on(type, fn);
			};
		});




		jQuery.expr.pseudos.animated = function (elem) {
			return jQuery.grep(jQuery.timers, function (fn) {
				return elem === fn.elem;
			}).length;
		};




		jQuery.offset = {
			setOffset: function (elem, options, i) {
				var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
					position = jQuery.css(elem, "position"),
					curElem = jQuery(elem),
					props = {};

				// Set position first, in-case top/left are set even on static elem
				if (position === "static") {
					elem.style.position = "relative";
				}

				curOffset = curElem.offset();
				curCSSTop = jQuery.css(elem, "top");
				curCSSLeft = jQuery.css(elem, "left");
				calculatePosition = (position === "absolute" || position === "fixed") &&
					(curCSSTop + curCSSLeft).indexOf("auto") > -1;

				// Need to be able to calculate position if either
				// top or left is auto and position is either absolute or fixed
				if (calculatePosition) {
					curPosition = curElem.position();
					curTop = curPosition.top;
					curLeft = curPosition.left;

				} else {
					curTop = parseFloat(curCSSTop) || 0;
					curLeft = parseFloat(curCSSLeft) || 0;
				}

				if (jQuery.isFunction(options)) {

					// Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
					options = options.call(elem, i, jQuery.extend({}, curOffset));
				}

				if (options.top != null) {
					props.top = (options.top - curOffset.top) + curTop;
				}
				if (options.left != null) {
					props.left = (options.left - curOffset.left) + curLeft;
				}

				if ("using" in options) {
					options.using.call(elem, props);

				} else {
					curElem.css(props);
				}
			}
		};

		jQuery.fn.extend({
			offset: function (options) {

				// Preserve chaining for setter
				if (arguments.length) {
					return options === undefined ?
						this :
						this.each(function (i) {
							jQuery.offset.setOffset(this, options, i);
						});
				}

				var doc, docElem, rect, win,
					elem = this[0];

				if (!elem) {
					return;
				}

				// Return zeros for disconnected and hidden (display: none) elements (gh-2310)
				// Support: IE <=11 only
				// Running getBoundingClientRect on a
				// disconnected node in IE throws an error
				if (!elem.getClientRects().length) {
					return { top: 0, left: 0 };
				}

				rect = elem.getBoundingClientRect();

				doc = elem.ownerDocument;
				docElem = doc.documentElement;
				win = doc.defaultView;

				return {
					top: rect.top + win.pageYOffset - docElem.clientTop,
					left: rect.left + win.pageXOffset - docElem.clientLeft
				};
			},

			position: function () {
				if (!this[0]) {
					return;
				}

				var offsetParent, offset,
					elem = this[0],
					parentOffset = { top: 0, left: 0 };

				// Fixed elements are offset from window (parentOffset = {top:0, left: 0},
				// because it is its only offset parent
				if (jQuery.css(elem, "position") === "fixed") {

					// Assume getBoundingClientRect is there when computed position is fixed
					offset = elem.getBoundingClientRect();

				} else {

					// Get *real* offsetParent
					offsetParent = this.offsetParent();

					// Get correct offsets
					offset = this.offset();
					if (!nodeName(offsetParent[0], "html")) {
						parentOffset = offsetParent.offset();
					}

					// Add offsetParent borders
					parentOffset = {
						top: parentOffset.top + jQuery.css(offsetParent[0], "borderTopWidth", true),
						left: parentOffset.left + jQuery.css(offsetParent[0], "borderLeftWidth", true)
					};
				}

				// Subtract parent offsets and element margins
				return {
					top: offset.top - parentOffset.top - jQuery.css(elem, "marginTop", true),
					left: offset.left - parentOffset.left - jQuery.css(elem, "marginLeft", true)
				};
			},

			// This method will return documentElement in the following cases:
			// 1) For the element inside the iframe without offsetParent, this method will return
			//    documentElement of the parent window
			// 2) For the hidden or detached element
			// 3) For body or html element, i.e. in case of the html node - it will return itself
			//
			// but those exceptions were never presented as a real life use-cases
			// and might be considered as more preferable results.
			//
			// This logic, however, is not guaranteed and can change at any point in the future
			offsetParent: function () {
				return this.map(function () {
					var offsetParent = this.offsetParent;

					while (offsetParent && jQuery.css(offsetParent, "position") === "static") {
						offsetParent = offsetParent.offsetParent;
					}

					return offsetParent || documentElement;
				});
			}
		});

		// Create scrollLeft and scrollTop methods
		jQuery.each({ scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function (method, prop) {
			var top = "pageYOffset" === prop;

			jQuery.fn[method] = function (val) {
				return access(this, function (elem, method, val) {

					// Coalesce documents and windows
					var win;
					if (jQuery.isWindow(elem)) {
						win = elem;
					} else if (elem.nodeType === 9) {
						win = elem.defaultView;
					}

					if (val === undefined) {
						return win ? win[prop] : elem[method];
					}

					if (win) {
						win.scrollTo(
							!top ? val : win.pageXOffset,
							top ? val : win.pageYOffset
						);

					} else {
						elem[method] = val;
					}
				}, method, val, arguments.length);
			};
		});

		// Support: Safari <=7 - 9.1, Chrome <=37 - 49
		// Add the top/left cssHooks using jQuery.fn.position
		// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
		// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
		// getComputedStyle returns percent when specified for top/left/bottom/right;
		// rather than make the css module depend on the offset module, just check for it here
		jQuery.each(["top", "left"], function (i, prop) {
			jQuery.cssHooks[prop] = addGetHookIf(support.pixelPosition,
				function (elem, computed) {
					if (computed) {
						computed = curCSS(elem, prop);

						// If curCSS returns percentage, fallback to offset
						return rnumnonpx.test(computed) ?
							jQuery(elem).position()[prop] + "px" :
							computed;
					}
				}
			);
		});


		// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
		jQuery.each({ Height: "height", Width: "width" }, function (name, type) {
			jQuery.each({ padding: "inner" + name, content: type, "": "outer" + name },
				function (defaultExtra, funcName) {

					// Margin is only for outerHeight, outerWidth
					jQuery.fn[funcName] = function (margin, value) {
						var chainable = arguments.length && (defaultExtra || typeof margin !== "boolean"),
							extra = defaultExtra || (margin === true || value === true ? "margin" : "border");

						return access(this, function (elem, type, value) {
							var doc;

							if (jQuery.isWindow(elem)) {

								// $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
								return funcName.indexOf("outer") === 0 ?
									elem["inner" + name] :
									elem.document.documentElement["client" + name];
							}

							// Get document width or height
							if (elem.nodeType === 9) {
								doc = elem.documentElement;

								// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
								// whichever is greatest
								return Math.max(
									elem.body["scroll" + name], doc["scroll" + name],
									elem.body["offset" + name], doc["offset" + name],
									doc["client" + name]
								);
							}

							return value === undefined ?

								// Get width or height on the element, requesting but not forcing parseFloat
								jQuery.css(elem, type, extra) :

								// Set width or height on the element
								jQuery.style(elem, type, value, extra);
						}, type, chainable ? margin : undefined, chainable);
					};
				});
		});


		jQuery.fn.extend({

			bind: function (types, data, fn) {
				return this.on(types, null, data, fn);
			},
			unbind: function (types, fn) {
				return this.off(types, null, fn);
			},

			delegate: function (selector, types, data, fn) {
				return this.on(types, selector, data, fn);
			},
			undelegate: function (selector, types, fn) {

				// ( namespace ) or ( selector, types [, fn] )
				return arguments.length === 1 ?
					this.off(selector, "**") :
					this.off(types, selector || "**", fn);
			}
		});

		jQuery.holdReady = function (hold) {
			if (hold) {
				jQuery.readyWait++;
			} else {
				jQuery.ready(true);
			}
		};
		jQuery.isArray = Array.isArray;
		jQuery.parseJSON = JSON.parse;
		jQuery.nodeName = nodeName;




		// Register as a named AMD module, since jQuery can be concatenated with other
		// files that may use define, but not via a proper concatenation script that
		// understands anonymous AMD modules. A named AMD is safest and most robust
		// way to register. Lowercase jquery is used because AMD module names are
		// derived from file names, and jQuery is normally delivered in a lowercase
		// file name. Do this after creating the global so that if an AMD module wants
		// to call noConflict to hide this version of jQuery, it will work.

		// Note that for maximum portability, libraries that are not jQuery should
		// declare themselves as anonymous modules, and avoid setting a global if an
		// AMD loader is present. jQuery is a special case. For more information, see
		// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon

		if (typeof undefined === "function" && undefined.amd) {
			undefined("jquery", [], function () {
				return jQuery;
			});
		}




		var

			// Map over jQuery in case of overwrite
			_jQuery = window.jQuery,

			// Map over the $ in case of overwrite
			_$ = window.$;

		jQuery.noConflict = function (deep) {
			if (window.$ === jQuery) {
				window.$ = _$;
			}

			if (deep && window.jQuery === jQuery) {
				window.jQuery = _jQuery;
			}

			return jQuery;
		};

		// Expose jQuery and $ identifiers, even in AMD
		// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
		// and CommonJS for browser emulators (#13566)
		if (!noGlobal) {
			window.jQuery = window.$ = jQuery;
		}




		return jQuery;
	});
});

var mousetrap = createCommonjsModule(function (module) {
	/*global define:false */
	/**
	 * Copyright 2012-2017 Craig Campbell
	 *
	 * Licensed under the Apache License, Version 2.0 (the "License");
	 * you may not use this file except in compliance with the License.
	 * You may obtain a copy of the License at
	 *
	 * http://www.apache.org/licenses/LICENSE-2.0
	 *
	 * Unless required by applicable law or agreed to in writing, software
	 * distributed under the License is distributed on an "AS IS" BASIS,
	 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	 * See the License for the specific language governing permissions and
	 * limitations under the License.
	 *
	 * Mousetrap is a simple keyboard shortcut library for Javascript with
	 * no external dependencies
	 *
	 * @version 1.6.1
	 * @url craig.is/killing/mice
	 */
	(function (window, document, undefined) {

		// Check if mousetrap is used inside browser, if not, return
		if (!window) {
			return;
		}

		/**
		 * mapping of special keycodes to their corresponding keys
		 *
		 * everything in this dictionary cannot use keypress events
		 * so it has to be here to map to the correct keycodes for
		 * keyup/keydown events
		 *
		 * @type {Object}
		 */
		var _MAP = {
			8: 'backspace',
			9: 'tab',
			13: 'enter',
			16: 'shift',
			17: 'ctrl',
			18: 'alt',
			20: 'capslock',
			27: 'esc',
			32: 'space',
			33: 'pageup',
			34: 'pagedown',
			35: 'end',
			36: 'home',
			37: 'left',
			38: 'up',
			39: 'right',
			40: 'down',
			45: 'ins',
			46: 'del',
			91: 'meta',
			93: 'meta',
			224: 'meta'
		};

		/**
		 * mapping for special characters so they can support
		 *
		 * this dictionary is only used incase you want to bind a
		 * keyup or keydown event to one of these keys
		 *
		 * @type {Object}
		 */
		var _KEYCODE_MAP = {
			106: '*',
			107: '+',
			109: '-',
			110: '.',
			111: '/',
			186: ';',
			187: '=',
			188: ',',
			189: '-',
			190: '.',
			191: '/',
			192: '`',
			219: '[',
			220: '\\',
			221: ']',
			222: '\''
		};

		/**
		 * this is a mapping of keys that require shift on a US keypad
		 * back to the non shift equivelents
		 *
		 * this is so you can use keyup events with these keys
		 *
		 * note that this will only work reliably on US keyboards
		 *
		 * @type {Object}
		 */
		var _SHIFT_MAP = {
			'~': '`',
			'!': '1',
			'@': '2',
			'#': '3',
			'$': '4',
			'%': '5',
			'^': '6',
			'&': '7',
			'*': '8',
			'(': '9',
			')': '0',
			'_': '-',
			'+': '=',
			':': ';',
			'\"': '\'',
			'<': ',',
			'>': '.',
			'?': '/',
			'|': '\\'
		};

		/**
		 * this is a list of special strings you can use to map
		 * to modifier keys when you specify your keyboard shortcuts
		 *
		 * @type {Object}
		 */
		var _SPECIAL_ALIASES = {
			'option': 'alt',
			'command': 'meta',
			'return': 'enter',
			'escape': 'esc',
			'plus': '+',
			'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'
		};

		/**
		 * variable to store the flipped version of _MAP from above
		 * needed to check if we should use keypress or not when no action
		 * is specified
		 *
		 * @type {Object|undefined}
		 */
		var _REVERSE_MAP;

		/**
		 * loop through the f keys, f1 to f19 and add them to the map
		 * programatically
		 */
		for (var i = 1; i < 20; ++i) {
			_MAP[111 + i] = 'f' + i;
		}

		/**
		 * loop through to map numbers on the numeric keypad
		 */
		for (i = 0; i <= 9; ++i) {

			// This needs to use a string cause otherwise since 0 is falsey
			// mousetrap will never fire for numpad 0 pressed as part of a keydown
			// event.
			//
			// @see https://github.com/ccampbell/mousetrap/pull/258
			_MAP[i + 96] = i.toString();
		}

		/**
		 * cross browser add event method
		 *
		 * @param {Element|HTMLDocument} object
		 * @param {string} type
		 * @param {Function} callback
		 * @returns void
		 */
		function _addEvent(object, type, callback) {
			if (object.addEventListener) {
				object.addEventListener(type, callback, false);
				return;
			}

			object.attachEvent('on' + type, callback);
		}

		/**
		 * takes the event and returns the key character
		 *
		 * @param {Event} e
		 * @return {string}
		 */
		function _characterFromEvent(e) {

			// for keypress events we should return the character as is
			if (e.type == 'keypress') {
				var character = String.fromCharCode(e.which);

				// if the shift key is not pressed then it is safe to assume
				// that we want the character to be lowercase.  this means if
				// you accidentally have caps lock on then your key bindings
				// will continue to work
				//
				// the only side effect that might not be desired is if you
				// bind something like 'A' cause you want to trigger an
				// event when capital A is pressed caps lock will no longer
				// trigger the event.  shift+a will though.
				if (!e.shiftKey) {
					character = character.toLowerCase();
				}

				return character;
			}

			// for non keypress events the special maps are needed
			if (_MAP[e.which]) {
				return _MAP[e.which];
			}

			if (_KEYCODE_MAP[e.which]) {
				return _KEYCODE_MAP[e.which];
			}

			// if it is not in the special map

			// with keydown and keyup events the character seems to always
			// come in as an uppercase character whether you are pressing shift
			// or not.  we should make sure it is always lowercase for comparisons
			return String.fromCharCode(e.which).toLowerCase();
		}

		/**
		 * checks if two arrays are equal
		 *
		 * @param {Array} modifiers1
		 * @param {Array} modifiers2
		 * @returns {boolean}
		 */
		function _modifiersMatch(modifiers1, modifiers2) {
			return modifiers1.sort().join(',') === modifiers2.sort().join(',');
		}

		/**
		 * takes a key event and figures out what the modifiers are
		 *
		 * @param {Event} e
		 * @returns {Array}
		 */
		function _eventModifiers(e) {
			var modifiers = [];

			if (e.shiftKey) {
				modifiers.push('shift');
			}

			if (e.altKey) {
				modifiers.push('alt');
			}

			if (e.ctrlKey) {
				modifiers.push('ctrl');
			}

			if (e.metaKey) {
				modifiers.push('meta');
			}

			return modifiers;
		}

		/**
		 * prevents default for this event
		 *
		 * @param {Event} e
		 * @returns void
		 */
		function _preventDefault(e) {
			if (e.preventDefault) {
				e.preventDefault();
				return;
			}

			e.returnValue = false;
		}

		/**
		 * stops propogation for this event
		 *
		 * @param {Event} e
		 * @returns void
		 */
		function _stopPropagation(e) {
			if (e.stopPropagation) {
				e.stopPropagation();
				return;
			}

			e.cancelBubble = true;
		}

		/**
		 * determines if the keycode specified is a modifier key or not
		 *
		 * @param {string} key
		 * @returns {boolean}
		 */
		function _isModifier(key) {
			return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
		}

		/**
		 * reverses the map lookup so that we can look for specific keys
		 * to see what can and can't use keypress
		 *
		 * @return {Object}
		 */
		function _getReverseMap() {
			if (!_REVERSE_MAP) {
				_REVERSE_MAP = {};
				for (var key in _MAP) {

					// pull out the numeric keypad from here cause keypress should
					// be able to detect the keys from the character
					if (key > 95 && key < 112) {
						continue;
					}

					if (_MAP.hasOwnProperty(key)) {
						_REVERSE_MAP[_MAP[key]] = key;
					}
				}
			}
			return _REVERSE_MAP;
		}

		/**
		 * picks the best action based on the key combination
		 *
		 * @param {string} key - character for key
		 * @param {Array} modifiers
		 * @param {string=} action passed in
		 */
		function _pickBestAction(key, modifiers, action) {

			// if no action was picked in we should try to pick the one
			// that we think would work best for this key
			if (!action) {
				action = _getReverseMap()[key] ? 'keydown' : 'keypress';
			}

			// modifier keys don't work as expected with keypress,
			// switch to keydown
			if (action == 'keypress' && modifiers.length) {
				action = 'keydown';
			}

			return action;
		}

		/**
		 * Converts from a string key combination to an array
		 *
		 * @param  {string} combination like "command+shift+l"
		 * @return {Array}
		 */
		function _keysFromString(combination) {
			if (combination === '+') {
				return ['+'];
			}

			combination = combination.replace(/\+{2}/g, '+plus');
			return combination.split('+');
		}

		/**
		 * Gets info for a specific key combination
		 *
		 * @param  {string} combination key combination ("command+s" or "a" or "*")
		 * @param  {string=} action
		 * @returns {Object}
		 */
		function _getKeyInfo(combination, action) {
			var keys;
			var key;
			var i;
			var modifiers = [];

			// take the keys from this pattern and figure out what the actual
			// pattern is all about
			keys = _keysFromString(combination);

			for (i = 0; i < keys.length; ++i) {
				key = keys[i];

				// normalize key names
				if (_SPECIAL_ALIASES[key]) {
					key = _SPECIAL_ALIASES[key];
				}

				// if this is not a keypress event then we should
				// be smart about using shift keys
				// this will only work for US keyboards however
				if (action && action != 'keypress' && _SHIFT_MAP[key]) {
					key = _SHIFT_MAP[key];
					modifiers.push('shift');
				}

				// if this key is a modifier then add it to the list of modifiers
				if (_isModifier(key)) {
					modifiers.push(key);
				}
			}

			// depending on what the key combination is
			// we will try to pick the best event for it
			action = _pickBestAction(key, modifiers, action);

			return {
				key: key,
				modifiers: modifiers,
				action: action
			};
		}

		function _belongsTo(element, ancestor) {
			if (element === null || element === document) {
				return false;
			}

			if (element === ancestor) {
				return true;
			}

			return _belongsTo(element.parentNode, ancestor);
		}

		function Mousetrap(targetElement) {
			var self = this;

			targetElement = targetElement || document;

			if (!(self instanceof Mousetrap)) {
				return new Mousetrap(targetElement);
			}

			/**
			 * element to attach key events to
			 *
			 * @type {Element}
			 */
			self.target = targetElement;

			/**
			 * a list of all the callbacks setup via Mousetrap.bind()
			 *
			 * @type {Object}
			 */
			self._callbacks = {};

			/**
			 * direct map of string combinations to callbacks used for trigger()
			 *
			 * @type {Object}
			 */
			self._directMap = {};

			/**
			 * keeps track of what level each sequence is at since multiple
			 * sequences can start out with the same sequence
			 *
			 * @type {Object}
			 */
			var _sequenceLevels = {};

			/**
			 * variable to store the setTimeout call
			 *
			 * @type {null|number}
			 */
			var _resetTimer;

			/**
			 * temporary state where we will ignore the next keyup
			 *
			 * @type {boolean|string}
			 */
			var _ignoreNextKeyup = false;

			/**
			 * temporary state where we will ignore the next keypress
			 *
			 * @type {boolean}
			 */
			var _ignoreNextKeypress = false;

			/**
			 * are we currently inside of a sequence?
			 * type of action ("keyup" or "keydown" or "keypress") or false
			 *
			 * @type {boolean|string}
			 */
			var _nextExpectedAction = false;

			/**
			 * resets all sequence counters except for the ones passed in
			 *
			 * @param {Object} doNotReset
			 * @returns void
			 */
			function _resetSequences(doNotReset) {
				doNotReset = doNotReset || {};

				var activeSequences = false,
					key;

				for (key in _sequenceLevels) {
					if (doNotReset[key]) {
						activeSequences = true;
						continue;
					}
					_sequenceLevels[key] = 0;
				}

				if (!activeSequences) {
					_nextExpectedAction = false;
				}
			}

			/**
			 * finds all callbacks that match based on the keycode, modifiers,
			 * and action
			 *
			 * @param {string} character
			 * @param {Array} modifiers
			 * @param {Event|Object} e
			 * @param {string=} sequenceName - name of the sequence we are looking for
			 * @param {string=} combination
			 * @param {number=} level
			 * @returns {Array}
			 */
			function _getMatches(character, modifiers, e, sequenceName, combination, level) {
				var i;
				var callback;
				var matches = [];
				var action = e.type;

				// if there are no events related to this keycode
				if (!self._callbacks[character]) {
					return [];
				}

				// if a modifier key is coming up on its own we should allow it
				if (action == 'keyup' && _isModifier(character)) {
					modifiers = [character];
				}

				// loop through all callbacks for the key that was pressed
				// and see if any of them match
				for (i = 0; i < self._callbacks[character].length; ++i) {
					callback = self._callbacks[character][i];

					// if a sequence name is not specified, but this is a sequence at
					// the wrong level then move onto the next match
					if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {
						continue;
					}

					// if the action we are looking for doesn't match the action we got
					// then we should keep going
					if (action != callback.action) {
						continue;
					}

					// if this is a keypress event and the meta key and control key
					// are not pressed that means that we need to only look at the
					// character, otherwise check the modifiers as well
					//
					// chrome will not fire a keypress if meta or control is down
					// safari will fire a keypress if meta or meta+shift is down
					// firefox will fire a keypress if meta or control is down
					if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) {

						// when you bind a combination or sequence a second time it
						// should overwrite the first one.  if a sequenceName or
						// combination is specified in this call it does just that
						//
						// @todo make deleting its own method?
						var deleteCombo = !sequenceName && callback.combo == combination;
						var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;
						if (deleteCombo || deleteSequence) {
							self._callbacks[character].splice(i, 1);
						}

						matches.push(callback);
					}
				}

				return matches;
			}

			/**
			 * actually calls the callback function
			 *
			 * if your callback function returns false this will use the jquery
			 * convention - prevent default and stop propogation on the event
			 *
			 * @param {Function} callback
			 * @param {Event} e
			 * @returns void
			 */
			function _fireCallback(callback, e, combo, sequence) {

				// if this event should not happen stop here
				if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
					return;
				}

				if (callback(e, combo) === false) {
					_preventDefault(e);
					_stopPropagation(e);
				}
			}

			/**
			 * handles a character key event
			 *
			 * @param {string} character
			 * @param {Array} modifiers
			 * @param {Event} e
			 * @returns void
			 */
			self._handleKey = function (character, modifiers, e) {
				var callbacks = _getMatches(character, modifiers, e);
				var i;
				var doNotReset = {};
				var maxLevel = 0;
				var processedSequenceCallback = false;

				// Calculate the maxLevel for sequences so we can only execute the longest callback sequence
				for (i = 0; i < callbacks.length; ++i) {
					if (callbacks[i].seq) {
						maxLevel = Math.max(maxLevel, callbacks[i].level);
					}
				}

				// loop through matching callbacks for this key event
				for (i = 0; i < callbacks.length; ++i) {

					// fire for all sequence callbacks
					// this is because if for example you have multiple sequences
					// bound such as "g i" and "g t" they both need to fire the
					// callback for matching g cause otherwise you can only ever
					// match the first one
					if (callbacks[i].seq) {

						// only fire callbacks for the maxLevel to prevent
						// subsequences from also firing
						//
						// for example 'a option b' should not cause 'option b' to fire
						// even though 'option b' is part of the other sequence
						//
						// any sequences that do not match here will be discarded
						// below by the _resetSequences call
						if (callbacks[i].level != maxLevel) {
							continue;
						}

						processedSequenceCallback = true;

						// keep a list of which sequences were matches for later
						doNotReset[callbacks[i].seq] = 1;
						_fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);
						continue;
					}

					// if there were no sequence matches but we are still here
					// that means this is a regular match so we should fire that
					if (!processedSequenceCallback) {
						_fireCallback(callbacks[i].callback, e, callbacks[i].combo);
					}
				}

				// if the key you pressed matches the type of sequence without
				// being a modifier (ie "keyup" or "keypress") then we should
				// reset all sequences that were not matched by this event
				//
				// this is so, for example, if you have the sequence "h a t" and you
				// type "h e a r t" it does not match.  in this case the "e" will
				// cause the sequence to reset
				//
				// modifier keys are ignored because you can have a sequence
				// that contains modifiers such as "enter ctrl+space" and in most
				// cases the modifier key will be pressed before the next key
				//
				// also if you have a sequence such as "ctrl+b a" then pressing the
				// "b" key will trigger a "keypress" and a "keydown"
				//
				// the "keydown" is expected when there is a modifier, but the
				// "keypress" ends up matching the _nextExpectedAction since it occurs
				// after and that causes the sequence to reset
				//
				// we ignore keypresses in a sequence that directly follow a keydown
				// for the same character
				var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;
				if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) {
					_resetSequences(doNotReset);
				}

				_ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';
			};

			/**
			 * handles a keydown event
			 *
			 * @param {Event} e
			 * @returns void
			 */
			function _handleKeyEvent(e) {

				// normalize e.which for key events
				// @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
				if (typeof e.which !== 'number') {
					e.which = e.keyCode;
				}

				var character = _characterFromEvent(e);

				// no character found then stop
				if (!character) {
					return;
				}

				// need to use === for the character check because the character can be 0
				if (e.type == 'keyup' && _ignoreNextKeyup === character) {
					_ignoreNextKeyup = false;
					return;
				}

				self.handleKey(character, _eventModifiers(e), e);
			}

			/**
			 * called to set a 1 second timeout on the specified sequence
			 *
			 * this is so after each key press in the sequence you have 1 second
			 * to press the next key before you have to start over
			 *
			 * @returns void
			 */
			function _resetSequenceTimer() {
				clearTimeout(_resetTimer);
				_resetTimer = setTimeout(_resetSequences, 1000);
			}

			/**
			 * binds a key sequence to an event
			 *
			 * @param {string} combo - combo specified in bind call
			 * @param {Array} keys
			 * @param {Function} callback
			 * @param {string=} action
			 * @returns void
			 */
			function _bindSequence(combo, keys, callback, action) {

				// start off by adding a sequence level record for this combination
				// and setting the level to 0
				_sequenceLevels[combo] = 0;

				/**
				 * callback to increase the sequence level for this sequence and reset
				 * all other sequences that were active
				 *
				 * @param {string} nextAction
				 * @returns {Function}
				 */
				function _increaseSequence(nextAction) {
					return function () {
						_nextExpectedAction = nextAction;
						++_sequenceLevels[combo];
						_resetSequenceTimer();
					};
				}

				/**
				 * wraps the specified callback inside of another function in order
				 * to reset all sequence counters as soon as this sequence is done
				 *
				 * @param {Event} e
				 * @returns void
				 */
				function _callbackAndReset(e) {
					_fireCallback(callback, e, combo);

					// we should ignore the next key up if the action is key down
					// or keypress.  this is so if you finish a sequence and
					// release the key the final key will not trigger a keyup
					if (action !== 'keyup') {
						_ignoreNextKeyup = _characterFromEvent(e);
					}

					// weird race condition if a sequence ends with the key
					// another sequence begins with
					setTimeout(_resetSequences, 10);
				}

				// loop through keys one at a time and bind the appropriate callback
				// function.  for any key leading up to the final one it should
				// increase the sequence. after the final, it should reset all sequences
				//
				// if an action is specified in the original bind call then that will
				// be used throughout.  otherwise we will pass the action that the
				// next key in the sequence should match.  this allows a sequence
				// to mix and match keypress and keydown events depending on which
				// ones are better suited to the key provided
				for (var i = 0; i < keys.length; ++i) {
					var isFinal = i + 1 === keys.length;
					var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);
					_bindSingle(keys[i], wrappedCallback, action, combo, i);
				}
			}

			/**
			 * binds a single keyboard combination
			 *
			 * @param {string} combination
			 * @param {Function} callback
			 * @param {string=} action
			 * @param {string=} sequenceName - name of sequence if part of sequence
			 * @param {number=} level - what part of the sequence the command is
			 * @returns void
			 */
			function _bindSingle(combination, callback, action, sequenceName, level) {

				// store a direct mapped reference for use with Mousetrap.trigger
				self._directMap[combination + ':' + action] = callback;

				// make sure multiple spaces in a row become a single space
				combination = combination.replace(/\s+/g, ' ');

				var sequence = combination.split(' ');
				var info;

				// if this pattern is a sequence of keys then run through this method
				// to reprocess each pattern one key at a time
				if (sequence.length > 1) {
					_bindSequence(combination, sequence, callback, action);
					return;
				}

				info = _getKeyInfo(combination, action);

				// make sure to initialize array if this is the first time
				// a callback is added for this key
				self._callbacks[info.key] = self._callbacks[info.key] || [];

				// remove an existing match if there is one
				_getMatches(info.key, info.modifiers, { type: info.action }, sequenceName, combination, level);

				// add this call back to the array
				// if it is a sequence put it at the beginning
				// if not put it at the end
				//
				// this is important because the way these are processed expects
				// the sequence ones to come first
				self._callbacks[info.key][sequenceName ? 'unshift' : 'push']({
					callback: callback,
					modifiers: info.modifiers,
					action: info.action,
					seq: sequenceName,
					level: level,
					combo: combination
				});
			}

			/**
			 * binds multiple combinations to the same callback
			 *
			 * @param {Array} combinations
			 * @param {Function} callback
			 * @param {string|undefined} action
			 * @returns void
			 */
			self._bindMultiple = function (combinations, callback, action) {
				for (var i = 0; i < combinations.length; ++i) {
					_bindSingle(combinations[i], callback, action);
				}
			};

			// start!
			_addEvent(targetElement, 'keypress', _handleKeyEvent);
			_addEvent(targetElement, 'keydown', _handleKeyEvent);
			_addEvent(targetElement, 'keyup', _handleKeyEvent);
		}

		/**
		 * binds an event to mousetrap
		 *
		 * can be a single key, a combination of keys separated with +,
		 * an array of keys, or a sequence of keys separated by spaces
		 *
		 * be sure to list the modifier keys first to make sure that the
		 * correct key ends up getting bound (the last key in the pattern)
		 *
		 * @param {string|Array} keys
		 * @param {Function} callback
		 * @param {string=} action - 'keypress', 'keydown', or 'keyup'
		 * @returns void
		 */
		Mousetrap.prototype.bind = function (keys, callback, action) {
			var self = this;
			keys = keys instanceof Array ? keys : [keys];
			self._bindMultiple.call(self, keys, callback, action);
			return self;
		};

		/**
		 * unbinds an event to mousetrap
		 *
		 * the unbinding sets the callback function of the specified key combo
		 * to an empty function and deletes the corresponding key in the
		 * _directMap dict.
		 *
		 * TODO: actually remove this from the _callbacks dictionary instead
		 * of binding an empty function
		 *
		 * the keycombo+action has to be exactly the same as
		 * it was defined in the bind method
		 *
		 * @param {string|Array} keys
		 * @param {string} action
		 * @returns void
		 */
		Mousetrap.prototype.unbind = function (keys, action) {
			var self = this;
			return self.bind.call(self, keys, function () { }, action);
		};

		/**
		 * triggers an event that has already been bound
		 *
		 * @param {string} keys
		 * @param {string=} action
		 * @returns void
		 */
		Mousetrap.prototype.trigger = function (keys, action) {
			var self = this;
			if (self._directMap[keys + ':' + action]) {
				self._directMap[keys + ':' + action]({}, keys);
			}
			return self;
		};

		/**
		 * resets the library back to its initial state.  this is useful
		 * if you want to clear out the current keyboard shortcuts and bind
		 * new ones - for example if you switch to another page
		 *
		 * @returns void
		 */
		Mousetrap.prototype.reset = function () {
			var self = this;
			self._callbacks = {};
			self._directMap = {};
			return self;
		};

		/**
		 * should we stop this event before firing off callbacks
		 *
		 * @param {Event} e
		 * @param {Element} element
		 * @return {boolean}
		 */
		Mousetrap.prototype.stopCallback = function (e, element) {
			var self = this;

			// if the element has the class "mousetrap" then no need to stop
			if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
				return false;
			}

			if (_belongsTo(element, self.target)) {
				return false;
			}

			// stop for input, select, and textarea
			return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;
		};

		/**
		 * exposes _handleKey publicly so it can be overwritten by extensions
		 */
		Mousetrap.prototype.handleKey = function () {
			var self = this;
			return self._handleKey.apply(self, arguments);
		};

		/**
		 * allow custom key mappings
		 */
		Mousetrap.addKeycodes = function (object) {
			for (var key in object) {
				if (object.hasOwnProperty(key)) {
					_MAP[key] = object[key];
				}
			}
			_REVERSE_MAP = null;
		};

		/**
		 * Init the global mousetrap functions
		 *
		 * This method is needed to allow the global mousetrap functions to work
		 * now that mousetrap is a constructor function.
		 */
		Mousetrap.init = function () {
			var documentMousetrap = Mousetrap(document);
			for (var method in documentMousetrap) {
				if (method.charAt(0) !== '_') {
					Mousetrap[method] = (function (method) {
						return function () {
							return documentMousetrap[method].apply(documentMousetrap, arguments);
						};
					}(method));
				}
			}
		};

		Mousetrap.init();

		// expose mousetrap to the global object
		window.Mousetrap = Mousetrap;

		// expose as a common js module
		if ('object' !== 'undefined' && module.exports) {
			module.exports = Mousetrap;
		}

		// expose mousetrap as an AMD module
		if (typeof undefined === 'function' && undefined.amd) {
			undefined(function () {
				return Mousetrap;
			});
		}
	})(typeof window !== 'undefined' ? window : null, typeof window !== 'undefined' ? document : null);
});

var bootstrapSlider = createCommonjsModule(function (module) {
	/*! =======================================================
						  VERSION  9.8.0              
	========================================================= */
	"use strict";

	var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

	/*! =========================================================
	 * bootstrap-slider.js
	 *
	 * Maintainers:
	 *		Kyle Kemp
	 *			- Twitter: @seiyria
	 *			- Github:  seiyria
	 *		Rohit Kalkur
	 *			- Twitter: @Rovolutionary
	 *			- Github:  rovolution
	 *
	 * =========================================================
	 *
	 * bootstrap-slider is released under the MIT License
	 * Copyright (c) 2017 Kyle Kemp, Rohit Kalkur, and contributors
	 *
	 * Permission is hereby granted, free of charge, to any person
	 * obtaining a copy of this software and associated documentation
	 * files (the "Software"), to deal in the Software without
	 * restriction, including without limitation the rights to use,
	 * copy, modify, merge, publish, distribute, sublicense, and/or sell
	 * copies of the Software, and to permit persons to whom the
	 * Software is furnished to do so, subject to the following
	 * conditions:
	 *
	 * The above copyright notice and this permission notice shall be
	 * included in all copies or substantial portions of the Software.
	 *
	 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
	 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
	 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
	 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
	 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
	 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
	 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
	 * OTHER DEALINGS IN THE SOFTWARE.
	 *
	 * ========================================================= */

	/**
	 * Bridget makes jQuery widgets
	 * v1.0.1
	 * MIT license
	 */
	var windowIsDefined = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object";

	(function (factory) {
		if (typeof undefined === "function" && undefined.amd) {
			undefined(["jquery"], factory);
		} else if ((_typeof(module)) === "object" && module.exports) {
			var jQuery;
			try {
				jQuery = jquery$1;
			} catch (err) {
				jQuery = null;
			}
			module.exports = factory(jQuery);
		} else if (window) {
			window.Slider = factory(window.jQuery);
		}
	})(function ($) {
		// Constants
		var NAMESPACE_MAIN = 'slider';
		var NAMESPACE_ALTERNATE = 'bootstrapSlider';

		// Polyfill console methods
		if (windowIsDefined && !window.console) {
			window.console = {};
		}
		if (windowIsDefined && !window.console.log) {
			window.console.log = function () { };
		}
		if (windowIsDefined && !window.console.warn) {
			window.console.warn = function () { };
		}

		// Reference to Slider constructor
		var Slider;

		(function ($) {

			'use strict';

			// -------------------------- utils -------------------------- //

			var slice = Array.prototype.slice;

			function noop() { }

			// -------------------------- definition -------------------------- //

			function defineBridget($) {

				// bail if no jQuery
				if (!$) {
					return;
				}

				// -------------------------- addOptionMethod -------------------------- //

				/**
		* adds option method -> $().plugin('option', {...})
		* @param {Function} PluginClass - constructor class
		*/
				function addOptionMethod(PluginClass) {
					// don't overwrite original option method
					if (PluginClass.prototype.option) {
						return;
					}

					// option setter
					PluginClass.prototype.option = function (opts) {
						// bail out if not an object
						if (!$.isPlainObject(opts)) {
							return;
						}
						this.options = $.extend(true, this.options, opts);
					};
				}

				// -------------------------- plugin bridge -------------------------- //

				// helper function for logging errors
				// $.error breaks jQuery chaining
				var logError = typeof console === 'undefined' ? noop : function (message) {
					console.error(message);
				};

				/**
		* jQuery plugin bridge, access methods like $elem.plugin('method')
		* @param {String} namespace - plugin name
		* @param {Function} PluginClass - constructor class
		*/
				function bridge(namespace, PluginClass) {
					// add to jQuery fn namespace
					$.fn[namespace] = function (options) {
						var this$1 = this;

						if (typeof options === 'string') {
							// call plugin method when first argument is a string
							// get arguments for method
							var args = slice.call(arguments, 1);

							for (var i = 0, len = this.length; i < len; i++) {
								var elem = this$1[i];
								var instance = $.data(elem, namespace);
								if (!instance) {
									logError("cannot call methods on " + namespace + " prior to initialization; " + "attempted to call '" + options + "'");
									continue;
								}
								if (!$.isFunction(instance[options]) || options.charAt(0) === '_') {
									logError("no such method '" + options + "' for " + namespace + " instance");
									continue;
								}

								// trigger method with arguments
								var returnValue = instance[options].apply(instance, args);

								// break look and return first value if provided
								if (returnValue !== undefined && returnValue !== instance) {
									return returnValue;
								}
							}
							// return this if no return value
							return this;
						} else {
							var objects = this.map(function () {
								var instance = $.data(this, namespace);
								if (instance) {
									// apply options & init
									instance.option(options);
									instance._init();
								} else {
									// initialize new instance
									instance = new PluginClass(this, options);
									$.data(this, namespace, instance);
								}
								return $(this);
							});

							if (!objects || objects.length > 1) {
								return objects;
							} else {
								return objects[0];
							}
						}
					};
				}

				// -------------------------- bridget -------------------------- //

				/**
		* converts a Prototypical class into a proper jQuery plugin
		*   the class must have a ._init method
		* @param {String} namespace - plugin name, used in $().pluginName
		* @param {Function} PluginClass - constructor class
		*/
				$.bridget = function (namespace, PluginClass) {
					addOptionMethod(PluginClass);
					bridge(namespace, PluginClass);
				};

				return $.bridget;
			}

			// get jquery from browser global
			defineBridget($);
		})($);

		/*************************************************
					BOOTSTRAP-SLIDER SOURCE CODE
		  **************************************************/

		(function ($) {

			var ErrorMsgs = {
				formatInvalidInputErrorMsg: function formatInvalidInputErrorMsg(input) {
					return "Invalid input value '" + input + "' passed in";
				},
				callingContextNotSliderInstance: "Calling context element does not have instance of Slider bound to it. Check your code to make sure the JQuery object returned from the call to the slider() initializer is calling the method"
			};

			var SliderScale = {
				linear: {
					toValue: function toValue(percentage) {
						var this$1 = this;

						var rawValue = percentage / 100 * (this.options.max - this.options.min);
						var shouldAdjustWithBase = true;
						if (this.options.ticks_positions.length > 0) {
							var minv,
								maxv,
								minp,
								maxp = 0;
							for (var i = 1; i < this.options.ticks_positions.length; i++) {
								if (percentage <= this$1.options.ticks_positions[i]) {
									minv = this$1.options.ticks[i - 1];
									minp = this$1.options.ticks_positions[i - 1];
									maxv = this$1.options.ticks[i];
									maxp = this$1.options.ticks_positions[i];

									break;
								}
							}
							var partialPercentage = (percentage - minp) / (maxp - minp);
							rawValue = minv + partialPercentage * (maxv - minv);
							shouldAdjustWithBase = false;
						}

						var adjustment = shouldAdjustWithBase ? this.options.min : 0;
						var value = adjustment + Math.round(rawValue / this.options.step) * this.options.step;
						if (value < this.options.min) {
							return this.options.min;
						} else if (value > this.options.max) {
							return this.options.max;
						} else {
							return value;
						}
					},
					toPercentage: function toPercentage(value) {
						var this$1 = this;

						if (this.options.max === this.options.min) {
							return 0;
						}

						if (this.options.ticks_positions.length > 0) {
							var minv,
								maxv,
								minp,
								maxp = 0;
							for (var i = 0; i < this.options.ticks.length; i++) {
								if (value <= this$1.options.ticks[i]) {
									minv = i > 0 ? this$1.options.ticks[i - 1] : 0;
									minp = i > 0 ? this$1.options.ticks_positions[i - 1] : 0;
									maxv = this$1.options.ticks[i];
									maxp = this$1.options.ticks_positions[i];

									break;
								}
							}
							if (i > 0) {
								var partialPercentage = (value - minv) / (maxv - minv);
								return minp + partialPercentage * (maxp - minp);
							}
						}

						return 100 * (value - this.options.min) / (this.options.max - this.options.min);
					}
				},

				logarithmic: {
					/* Based on http://stackoverflow.com/questions/846221/logarithmic-slider */
					toValue: function toValue(percentage) {
						var min = this.options.min === 0 ? 0 : Math.log(this.options.min);
						var max = Math.log(this.options.max);
						var value = Math.exp(min + (max - min) * percentage / 100);
						value = this.options.min + Math.round((value - this.options.min) / this.options.step) * this.options.step;
						/* Rounding to the nearest step could exceed the min or
		  * max, so clip to those values. */
						if (value < this.options.min) {
							return this.options.min;
						} else if (value > this.options.max) {
							return this.options.max;
						} else {
							return value;
						}
					},
					toPercentage: function toPercentage(value) {
						if (this.options.max === this.options.min) {
							return 0;
						} else {
							var max = Math.log(this.options.max);
							var min = this.options.min === 0 ? 0 : Math.log(this.options.min);
							var v = value === 0 ? 0 : Math.log(value);
							return 100 * (v - min) / (max - min);
						}
					}
				}
			};

			/*************************************************
								CONSTRUCTOR
			**************************************************/
			Slider = function Slider(element, options) {
				createNewSlider.call(this, element, options);
				return this;
			};

			function createNewSlider(element, options) {
				var this$1 = this;


				/*
			  The internal state object is used to store data about the current 'state' of slider.
			  This includes values such as the `value`, `enabled`, etc...
	   */
				this._state = {
					value: null,
					enabled: null,
					offset: null,
					size: null,
					percentage: null,
					inDrag: false,
					over: false
				};

				// The objects used to store the reference to the tick methods if ticks_tooltip is on
				this.ticksCallbackMap = {};
				this.handleCallbackMap = {};

				if (typeof element === "string") {
					this.element = document.querySelector(element);
				} else if (element instanceof HTMLElement) {
					this.element = element;
				}

				/*************************************************
								Process Options
			  **************************************************/
				options = options ? options : {};
				var optionTypes = Object.keys(this.defaultOptions);

				for (var i = 0; i < optionTypes.length; i++) {
					var optName = optionTypes[i];

					// First check if an option was passed in via the constructor
					var val = options[optName];
					// If no data attrib, then check data atrributes
					val = typeof val !== 'undefined' ? val : getDataAttrib(this$1.element, optName);
					// Finally, if nothing was specified, use the defaults
					val = val !== null ? val : this$1.defaultOptions[optName];

					// Set all options on the instance of the Slider
					if (!this$1.options) {
						this$1.options = {};
					}
					this$1.options[optName] = val;
				}

				// Check options.rtl
				if (this.options.rtl === 'auto') {
					this.options.rtl = window.getComputedStyle(this.element).direction === 'rtl';
				}

				/*
			  Validate `tooltip_position` against 'orientation`
			  - if `tooltip_position` is incompatible with orientation, swith it to a default compatible with specified `orientation`
					-- default for "vertical" -> "right", "left" if rtl
					-- default for "horizontal" -> "top"
	   */
				if (this.options.orientation === "vertical" && (this.options.tooltip_position === "top" || this.options.tooltip_position === "bottom")) {
					if (this.options.rtl) {
						this.options.tooltip_position = "left";
					} else {
						this.options.tooltip_position = "right";
					}
				} else if (this.options.orientation === "horizontal" && (this.options.tooltip_position === "left" || this.options.tooltip_position === "right")) {

					this.options.tooltip_position = "top";
				}

				function getDataAttrib(element, optName) {
					var dataName = "data-slider-" + optName.replace(/_/g, '-');
					var dataValString = element.getAttribute(dataName);

					try {
						return JSON.parse(dataValString);
					} catch (err) {
						return dataValString;
					}
				}

				/*************************************************
								Create Markup
			  **************************************************/

				var origWidth = this.element.style.width;
				var updateSlider = false;
				var parent = this.element.parentNode;
				var sliderTrackSelection;
				var sliderTrackLow, sliderTrackHigh;
				var sliderMinHandle;
				var sliderMaxHandle;

				if (this.sliderElem) {
					updateSlider = true;
				} else {
					/* Create elements needed for slider */
					this.sliderElem = document.createElement("div");
					this.sliderElem.className = "slider";

					/* Create slider track elements */
					var sliderTrack = document.createElement("div");
					sliderTrack.className = "slider-track";

					sliderTrackLow = document.createElement("div");
					sliderTrackLow.className = "slider-track-low";

					sliderTrackSelection = document.createElement("div");
					sliderTrackSelection.className = "slider-selection";

					sliderTrackHigh = document.createElement("div");
					sliderTrackHigh.className = "slider-track-high";

					sliderMinHandle = document.createElement("div");
					sliderMinHandle.className = "slider-handle min-slider-handle";
					sliderMinHandle.setAttribute('role', 'slider');
					sliderMinHandle.setAttribute('aria-valuemin', this.options.min);
					sliderMinHandle.setAttribute('aria-valuemax', this.options.max);

					sliderMaxHandle = document.createElement("div");
					sliderMaxHandle.className = "slider-handle max-slider-handle";
					sliderMaxHandle.setAttribute('role', 'slider');
					sliderMaxHandle.setAttribute('aria-valuemin', this.options.min);
					sliderMaxHandle.setAttribute('aria-valuemax', this.options.max);

					sliderTrack.appendChild(sliderTrackLow);
					sliderTrack.appendChild(sliderTrackSelection);
					sliderTrack.appendChild(sliderTrackHigh);

					/* Create highlight range elements */
					this.rangeHighlightElements = [];
					var rangeHighlightsOpts = this.options.rangeHighlights;
					if (Array.isArray(rangeHighlightsOpts) && rangeHighlightsOpts.length > 0) {
						for (var j = 0; j < rangeHighlightsOpts.length; j++) {
							var rangeHighlightElement = document.createElement("div");
							var customClassString = rangeHighlightsOpts[j].class || "";
							rangeHighlightElement.className = "slider-rangeHighlight slider-selection " + customClassString;
							this$1.rangeHighlightElements.push(rangeHighlightElement);
							sliderTrack.appendChild(rangeHighlightElement);
						}
					}

					/* Add aria-labelledby to handle's */
					var isLabelledbyArray = Array.isArray(this.options.labelledby);
					if (isLabelledbyArray && this.options.labelledby[0]) {
						sliderMinHandle.setAttribute('aria-labelledby', this.options.labelledby[0]);
					}
					if (isLabelledbyArray && this.options.labelledby[1]) {
						sliderMaxHandle.setAttribute('aria-labelledby', this.options.labelledby[1]);
					}
					if (!isLabelledbyArray && this.options.labelledby) {
						sliderMinHandle.setAttribute('aria-labelledby', this.options.labelledby);
						sliderMaxHandle.setAttribute('aria-labelledby', this.options.labelledby);
					}

					/* Create ticks */
					this.ticks = [];
					if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) {
						this.ticksContainer = document.createElement('div');
						this.ticksContainer.className = 'slider-tick-container';

						for (i = 0; i < this.options.ticks.length; i++) {
							var tick = document.createElement('div');
							tick.className = 'slider-tick';
							if (this$1.options.ticks_tooltip) {
								var tickListenerReference = this$1._addTickListener();
								var enterCallback = tickListenerReference.addMouseEnter(this$1, tick, i);
								var leaveCallback = tickListenerReference.addMouseLeave(this$1, tick);

								this$1.ticksCallbackMap[i] = {
									mouseEnter: enterCallback,
									mouseLeave: leaveCallback
								};
							}
							this$1.ticks.push(tick);
							this$1.ticksContainer.appendChild(tick);
						}

						sliderTrackSelection.className += " tick-slider-selection";
					}

					this.tickLabels = [];
					if (Array.isArray(this.options.ticks_labels) && this.options.ticks_labels.length > 0) {
						this.tickLabelContainer = document.createElement('div');
						this.tickLabelContainer.className = 'slider-tick-label-container';

						for (i = 0; i < this.options.ticks_labels.length; i++) {
							var label = document.createElement('div');
							var noTickPositionsSpecified = this$1.options.ticks_positions.length === 0;
							var tickLabelsIndex = this$1.options.reversed && noTickPositionsSpecified ? this$1.options.ticks_labels.length - (i + 1) : i;
							label.className = 'slider-tick-label';
							label.innerHTML = this$1.options.ticks_labels[tickLabelsIndex];

							this$1.tickLabels.push(label);
							this$1.tickLabelContainer.appendChild(label);
						}
					}

					var createAndAppendTooltipSubElements = function createAndAppendTooltipSubElements(tooltipElem) {
						var arrow = document.createElement("div");
						arrow.className = "tooltip-arrow";

						var inner = document.createElement("div");
						inner.className = "tooltip-inner";

						tooltipElem.appendChild(arrow);
						tooltipElem.appendChild(inner);
					};

					/* Create tooltip elements */
					var sliderTooltip = document.createElement("div");
					sliderTooltip.className = "tooltip tooltip-main";
					sliderTooltip.setAttribute('role', 'presentation');
					createAndAppendTooltipSubElements(sliderTooltip);

					var sliderTooltipMin = document.createElement("div");
					sliderTooltipMin.className = "tooltip tooltip-min";
					sliderTooltipMin.setAttribute('role', 'presentation');
					createAndAppendTooltipSubElements(sliderTooltipMin);

					var sliderTooltipMax = document.createElement("div");
					sliderTooltipMax.className = "tooltip tooltip-max";
					sliderTooltipMax.setAttribute('role', 'presentation');
					createAndAppendTooltipSubElements(sliderTooltipMax);

					/* Append components to sliderElem */
					this.sliderElem.appendChild(sliderTrack);
					this.sliderElem.appendChild(sliderTooltip);
					this.sliderElem.appendChild(sliderTooltipMin);
					this.sliderElem.appendChild(sliderTooltipMax);

					if (this.tickLabelContainer) {
						this.sliderElem.appendChild(this.tickLabelContainer);
					}
					if (this.ticksContainer) {
						this.sliderElem.appendChild(this.ticksContainer);
					}

					this.sliderElem.appendChild(sliderMinHandle);
					this.sliderElem.appendChild(sliderMaxHandle);

					/* Append slider element to parent container, right before the original <input> element */
					parent.insertBefore(this.sliderElem, this.element);

					/* Hide original <input> element */
					this.element.style.display = "none";
				}
				/* If JQuery exists, cache JQ references */
				if ($) {
					this.$element = $(this.element);
					this.$sliderElem = $(this.sliderElem);
				}

				/*************************************************
									Setup
			  **************************************************/
				this.eventToCallbackMap = {};
				this.sliderElem.id = this.options.id;

				this.touchCapable = 'ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch;

				this.touchX = 0;
				this.touchY = 0;

				this.tooltip = this.sliderElem.querySelector('.tooltip-main');
				this.tooltipInner = this.tooltip.querySelector('.tooltip-inner');

				this.tooltip_min = this.sliderElem.querySelector('.tooltip-min');
				this.tooltipInner_min = this.tooltip_min.querySelector('.tooltip-inner');

				this.tooltip_max = this.sliderElem.querySelector('.tooltip-max');
				this.tooltipInner_max = this.tooltip_max.querySelector('.tooltip-inner');

				if (SliderScale[this.options.scale]) {
					this.options.scale = SliderScale[this.options.scale];
				}

				if (updateSlider === true) {
					// Reset classes
					this._removeClass(this.sliderElem, 'slider-horizontal');
					this._removeClass(this.sliderElem, 'slider-vertical');
					this._removeClass(this.sliderElem, 'slider-rtl');
					this._removeClass(this.tooltip, 'hide');
					this._removeClass(this.tooltip_min, 'hide');
					this._removeClass(this.tooltip_max, 'hide');

					// Undo existing inline styles for track
					["left", "right", "top", "width", "height"].forEach(function (prop) {
						this._removeProperty(this.trackLow, prop);
						this._removeProperty(this.trackSelection, prop);
						this._removeProperty(this.trackHigh, prop);
					}, this);

					// Undo inline styles on handles
					[this.handle1, this.handle2].forEach(function (handle) {
						this._removeProperty(handle, 'left');
						this._removeProperty(handle, 'right');
						this._removeProperty(handle, 'top');
					}, this);

					// Undo inline styles and classes on tooltips
					[this.tooltip, this.tooltip_min, this.tooltip_max].forEach(function (tooltip) {
						this._removeProperty(tooltip, 'left');
						this._removeProperty(tooltip, 'right');
						this._removeProperty(tooltip, 'top');
						this._removeProperty(tooltip, 'margin-left');
						this._removeProperty(tooltip, 'margin-right');
						this._removeProperty(tooltip, 'margin-top');

						this._removeClass(tooltip, 'right');
						this._removeClass(tooltip, 'left');
						this._removeClass(tooltip, 'top');
					}, this);
				}

				if (this.options.orientation === 'vertical') {
					this._addClass(this.sliderElem, 'slider-vertical');
					this.stylePos = 'top';
					this.mousePos = 'pageY';
					this.sizePos = 'offsetHeight';
				} else {
					this._addClass(this.sliderElem, 'slider-horizontal');
					this.sliderElem.style.width = origWidth;
					this.options.orientation = 'horizontal';
					if (this.options.rtl) {
						this.stylePos = 'right';
					} else {
						this.stylePos = 'left';
					}
					this.mousePos = 'pageX';
					this.sizePos = 'offsetWidth';
				}
				// specific rtl class
				if (this.options.rtl) {
					this._addClass(this.sliderElem, 'slider-rtl');
				}
				this._setTooltipPosition();
				/* In case ticks are specified, overwrite the min and max bounds */
				if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) {
					this.options.max = Math.max.apply(Math, this.options.ticks);
					this.options.min = Math.min.apply(Math, this.options.ticks);
				}

				if (Array.isArray(this.options.value)) {
					this.options.range = true;
					this._state.value = this.options.value;
				} else if (this.options.range) {
					// User wants a range, but value is not an array
					this._state.value = [this.options.value, this.options.max];
				} else {
					this._state.value = this.options.value;
				}

				this.trackLow = sliderTrackLow || this.trackLow;
				this.trackSelection = sliderTrackSelection || this.trackSelection;
				this.trackHigh = sliderTrackHigh || this.trackHigh;

				if (this.options.selection === 'none') {
					this._addClass(this.trackLow, 'hide');
					this._addClass(this.trackSelection, 'hide');
					this._addClass(this.trackHigh, 'hide');
				} else if (this.options.selection === 'after' || this.options.selection === 'before') {
					this._removeClass(this.trackLow, 'hide');
					this._removeClass(this.trackSelection, 'hide');
					this._removeClass(this.trackHigh, 'hide');
				}

				this.handle1 = sliderMinHandle || this.handle1;
				this.handle2 = sliderMaxHandle || this.handle2;

				if (updateSlider === true) {
					// Reset classes
					this._removeClass(this.handle1, 'round triangle');
					this._removeClass(this.handle2, 'round triangle hide');

					for (i = 0; i < this.ticks.length; i++) {
						this$1._removeClass(this$1.ticks[i], 'round triangle hide');
					}
				}

				var availableHandleModifiers = ['round', 'triangle', 'custom'];
				var isValidHandleType = availableHandleModifiers.indexOf(this.options.handle) !== -1;
				if (isValidHandleType) {
					this._addClass(this.handle1, this.options.handle);
					this._addClass(this.handle2, this.options.handle);

					for (i = 0; i < this.ticks.length; i++) {
						this$1._addClass(this$1.ticks[i], this$1.options.handle);
					}
				}

				this._state.offset = this._offset(this.sliderElem);
				this._state.size = this.sliderElem[this.sizePos];
				this.setValue(this._state.value);

				/******************************************
							Bind Event Listeners
			  ******************************************/

				// Bind keyboard handlers
				this.handle1Keydown = this._keydown.bind(this, 0);
				this.handle1.addEventListener("keydown", this.handle1Keydown, false);

				this.handle2Keydown = this._keydown.bind(this, 1);
				this.handle2.addEventListener("keydown", this.handle2Keydown, false);

				this.mousedown = this._mousedown.bind(this);
				this.touchstart = this._touchstart.bind(this);
				this.touchmove = this._touchmove.bind(this);

				if (this.touchCapable) {
					// Test for passive event support
					var supportsPassive = false;
					try {
						var opts = Object.defineProperty({}, 'passive', {
							get: function get() {
								supportsPassive = true;
							}
						});
						window.addEventListener("test", null, opts);
					} catch (e) { }
					// Use our detect's results. passive applied if supported, capture will be false either way.
					var eventOptions = supportsPassive ? { passive: true } : false;
					// Bind touch handlers
					this.sliderElem.addEventListener("touchstart", this.touchstart, eventOptions);
					this.sliderElem.addEventListener("touchmove", this.touchmove, eventOptions);
				}
				this.sliderElem.addEventListener("mousedown", this.mousedown, false);

				// Bind window handlers
				this.resize = this._resize.bind(this);
				window.addEventListener("resize", this.resize, false);

				// Bind tooltip-related handlers
				if (this.options.tooltip === 'hide') {
					this._addClass(this.tooltip, 'hide');
					this._addClass(this.tooltip_min, 'hide');
					this._addClass(this.tooltip_max, 'hide');
				} else if (this.options.tooltip === 'always') {
					this._showTooltip();
					this._alwaysShowTooltip = true;
				} else {
					this.showTooltip = this._showTooltip.bind(this);
					this.hideTooltip = this._hideTooltip.bind(this);

					if (this.options.ticks_tooltip) {
						var callbackHandle = this._addTickListener();
						//create handle1 listeners and store references in map
						var mouseEnter = callbackHandle.addMouseEnter(this, this.handle1);
						var mouseLeave = callbackHandle.addMouseLeave(this, this.handle1);
						this.handleCallbackMap.handle1 = {
							mouseEnter: mouseEnter,
							mouseLeave: mouseLeave
						};
						//create handle2 listeners and store references in map
						mouseEnter = callbackHandle.addMouseEnter(this, this.handle2);
						mouseLeave = callbackHandle.addMouseLeave(this, this.handle2);
						this.handleCallbackMap.handle2 = {
							mouseEnter: mouseEnter,
							mouseLeave: mouseLeave
						};
					} else {
						this.sliderElem.addEventListener("mouseenter", this.showTooltip, false);
						this.sliderElem.addEventListener("mouseleave", this.hideTooltip, false);
					}

					this.handle1.addEventListener("focus", this.showTooltip, false);
					this.handle1.addEventListener("blur", this.hideTooltip, false);

					this.handle2.addEventListener("focus", this.showTooltip, false);
					this.handle2.addEventListener("blur", this.hideTooltip, false);
				}

				if (this.options.enabled) {
					this.enable();
				} else {
					this.disable();
				}
			}

			/*************************************************
						INSTANCE PROPERTIES/METHODS
			- Any methods bound to the prototype are considered
	  part of the plugin's `public` interface
			**************************************************/
			Slider.prototype = {
				_init: function _init() { }, // NOTE: Must exist to support bridget

				constructor: Slider,

				defaultOptions: {
					id: "",
					min: 0,
					max: 10,
					step: 1,
					precision: 0,
					orientation: 'horizontal',
					value: 5,
					range: false,
					selection: 'before',
					tooltip: 'show',
					tooltip_split: false,
					handle: 'round',
					reversed: false,
					rtl: 'auto',
					enabled: true,
					formatter: function formatter(val) {
						if (Array.isArray(val)) {
							return val[0] + " : " + val[1];
						} else {
							return val;
						}
					},
					natural_arrow_keys: false,
					ticks: [],
					ticks_positions: [],
					ticks_labels: [],
					ticks_snap_bounds: 0,
					ticks_tooltip: false,
					scale: 'linear',
					focus: false,
					tooltip_position: null,
					labelledby: null,
					rangeHighlights: []
				},

				getElement: function getElement() {
					return this.sliderElem;
				},

				getValue: function getValue() {
					if (this.options.range) {
						return this._state.value;
					} else {
						return this._state.value[0];
					}
				},

				setValue: function setValue(val, triggerSlideEvent, triggerChangeEvent) {
					if (!val) {
						val = 0;
					}
					var oldValue = this.getValue();
					this._state.value = this._validateInputValue(val);
					var applyPrecision = this._applyPrecision.bind(this);

					if (this.options.range) {
						this._state.value[0] = applyPrecision(this._state.value[0]);
						this._state.value[1] = applyPrecision(this._state.value[1]);

						this._state.value[0] = Math.max(this.options.min, Math.min(this.options.max, this._state.value[0]));
						this._state.value[1] = Math.max(this.options.min, Math.min(this.options.max, this._state.value[1]));
					} else {
						this._state.value = applyPrecision(this._state.value);
						this._state.value = [Math.max(this.options.min, Math.min(this.options.max, this._state.value))];
						this._addClass(this.handle2, 'hide');
						if (this.options.selection === 'after') {
							this._state.value[1] = this.options.max;
						} else {
							this._state.value[1] = this.options.min;
						}
					}

					if (this.options.max > this.options.min) {
						this._state.percentage = [this._toPercentage(this._state.value[0]), this._toPercentage(this._state.value[1]), this.options.step * 100 / (this.options.max - this.options.min)];
					} else {
						this._state.percentage = [0, 0, 100];
					}

					this._layout();
					var newValue = this.options.range ? this._state.value : this._state.value[0];

					this._setDataVal(newValue);
					if (triggerSlideEvent === true) {
						this._trigger('slide', newValue);
					}
					if (oldValue !== newValue && triggerChangeEvent === true) {
						this._trigger('change', {
							oldValue: oldValue,
							newValue: newValue
						});
					}

					return this;
				},

				destroy: function destroy() {
					// Remove event handlers on slider elements
					this._removeSliderEventHandlers();

					// Remove the slider from the DOM
					this.sliderElem.parentNode.removeChild(this.sliderElem);
					/* Show original <input> element */
					this.element.style.display = "";

					// Clear out custom event bindings
					this._cleanUpEventCallbacksMap();

					// Remove data values
					this.element.removeAttribute("data");

					// Remove JQuery handlers/data
					if ($) {
						this._unbindJQueryEventHandlers();
						this.$element.removeData('slider');
					}
				},

				disable: function disable() {
					this._state.enabled = false;
					this.handle1.removeAttribute("tabindex");
					this.handle2.removeAttribute("tabindex");
					this._addClass(this.sliderElem, 'slider-disabled');
					this._trigger('slideDisabled');

					return this;
				},

				enable: function enable() {
					this._state.enabled = true;
					this.handle1.setAttribute("tabindex", 0);
					this.handle2.setAttribute("tabindex", 0);
					this._removeClass(this.sliderElem, 'slider-disabled');
					this._trigger('slideEnabled');

					return this;
				},

				toggle: function toggle() {
					if (this._state.enabled) {
						this.disable();
					} else {
						this.enable();
					}
					return this;
				},

				isEnabled: function isEnabled() {
					return this._state.enabled;
				},

				on: function on(evt, callback) {
					this._bindNonQueryEventHandler(evt, callback);
					return this;
				},

				off: function off(evt, callback) {
					if ($) {
						this.$element.off(evt, callback);
						this.$sliderElem.off(evt, callback);
					} else {
						this._unbindNonQueryEventHandler(evt, callback);
					}
				},

				getAttribute: function getAttribute(attribute) {
					if (attribute) {
						return this.options[attribute];
					} else {
						return this.options;
					}
				},

				setAttribute: function setAttribute(attribute, value) {
					this.options[attribute] = value;
					return this;
				},

				refresh: function refresh() {
					this._removeSliderEventHandlers();
					createNewSlider.call(this, this.element, this.options);
					if ($) {
						// Bind new instance of slider to the element
						$.data(this.element, 'slider', this);
					}
					return this;
				},

				relayout: function relayout() {
					this._resize();
					this._layout();
					return this;
				},

				/******************************+
							HELPERS
			  - Any method that is not part of the public interface.
	   - Place it underneath this comment block and write its signature like so:
					_fnName : function() {...}
			  ********************************/
				_removeSliderEventHandlers: function _removeSliderEventHandlers() {
					var this$1 = this;

					// Remove keydown event listeners
					this.handle1.removeEventListener("keydown", this.handle1Keydown, false);
					this.handle2.removeEventListener("keydown", this.handle2Keydown, false);

					//remove the listeners from the ticks and handles if they had their own listeners
					if (this.options.ticks_tooltip) {
						var ticks = this.ticksContainer.getElementsByClassName('slider-tick');
						for (var i = 0; i < ticks.length; i++) {
							ticks[i].removeEventListener('mouseenter', this$1.ticksCallbackMap[i].mouseEnter, false);
							ticks[i].removeEventListener('mouseleave', this$1.ticksCallbackMap[i].mouseLeave, false);
						}
						this.handle1.removeEventListener('mouseenter', this.handleCallbackMap.handle1.mouseEnter, false);
						this.handle2.removeEventListener('mouseenter', this.handleCallbackMap.handle2.mouseEnter, false);
						this.handle1.removeEventListener('mouseleave', this.handleCallbackMap.handle1.mouseLeave, false);
						this.handle2.removeEventListener('mouseleave', this.handleCallbackMap.handle2.mouseLeave, false);
					}

					this.handleCallbackMap = null;
					this.ticksCallbackMap = null;

					if (this.showTooltip) {
						this.handle1.removeEventListener("focus", this.showTooltip, false);
						this.handle2.removeEventListener("focus", this.showTooltip, false);
					}
					if (this.hideTooltip) {
						this.handle1.removeEventListener("blur", this.hideTooltip, false);
						this.handle2.removeEventListener("blur", this.hideTooltip, false);
					}

					// Remove event listeners from sliderElem
					if (this.showTooltip) {
						this.sliderElem.removeEventListener("mouseenter", this.showTooltip, false);
					}
					if (this.hideTooltip) {
						this.sliderElem.removeEventListener("mouseleave", this.hideTooltip, false);
					}
					this.sliderElem.removeEventListener("touchstart", this.touchstart, false);
					this.sliderElem.removeEventListener("touchmove", this.touchmove, false);
					this.sliderElem.removeEventListener("mousedown", this.mousedown, false);

					// Remove window event listener
					window.removeEventListener("resize", this.resize, false);
				},
				_bindNonQueryEventHandler: function _bindNonQueryEventHandler(evt, callback) {
					if (this.eventToCallbackMap[evt] === undefined) {
						this.eventToCallbackMap[evt] = [];
					}
					this.eventToCallbackMap[evt].push(callback);
				},
				_unbindNonQueryEventHandler: function _unbindNonQueryEventHandler(evt, callback) {
					var callbacks = this.eventToCallbackMap[evt];
					if (callbacks !== undefined) {
						for (var i = 0; i < callbacks.length; i++) {
							if (callbacks[i] === callback) {
								callbacks.splice(i, 1);
								break;
							}
						}
					}
				},
				_cleanUpEventCallbacksMap: function _cleanUpEventCallbacksMap() {
					var this$1 = this;

					var eventNames = Object.keys(this.eventToCallbackMap);
					for (var i = 0; i < eventNames.length; i++) {
						var eventName = eventNames[i];
						delete this$1.eventToCallbackMap[eventName];
					}
				},
				_showTooltip: function _showTooltip() {
					if (this.options.tooltip_split === false) {
						this._addClass(this.tooltip, 'in');
						this.tooltip_min.style.display = 'none';
						this.tooltip_max.style.display = 'none';
					} else {
						this._addClass(this.tooltip_min, 'in');
						this._addClass(this.tooltip_max, 'in');
						this.tooltip.style.display = 'none';
					}
					this._state.over = true;
				},
				_hideTooltip: function _hideTooltip() {
					if (this._state.inDrag === false && this.alwaysShowTooltip !== true) {
						this._removeClass(this.tooltip, 'in');
						this._removeClass(this.tooltip_min, 'in');
						this._removeClass(this.tooltip_max, 'in');
					}
					this._state.over = false;
				},
				_setToolTipOnMouseOver: function _setToolTipOnMouseOver(tempState) {
					var formattedTooltipVal = this.options.formatter(!tempState ? this._state.value[0] : tempState.value[0]);
					var positionPercentages = !tempState ? getPositionPercentages(this._state, this.options.reversed) : getPositionPercentages(tempState, this.options.reversed);
					this._setText(this.tooltipInner, formattedTooltipVal);

					this.tooltip.style[this.stylePos] = positionPercentages[0] + "%";
					if (this.options.orientation === 'vertical') {
						this._css(this.tooltip, "margin-" + this.stylePos, -this.tooltip.offsetHeight / 2 + "px");
					} else {
						this._css(this.tooltip, "margin-" + this.stylePos, -this.tooltip.offsetWidth / 2 + "px");
					}

					function getPositionPercentages(state, reversed) {
						if (reversed) {
							return [100 - state.percentage[0], this.options.range ? 100 - state.percentage[1] : state.percentage[1]];
						}
						return [state.percentage[0], state.percentage[1]];
					}
				},
				_addTickListener: function _addTickListener() {
					return {
						addMouseEnter: function addMouseEnter(reference, tick, index) {
							var enter = function enter() {
								var tempState = reference._state;
								var idString = index >= 0 ? index : this.attributes['aria-valuenow'].value;
								var hoverIndex = parseInt(idString, 10);
								tempState.value[0] = hoverIndex;
								tempState.percentage[0] = reference.options.ticks_positions[hoverIndex];
								reference._setToolTipOnMouseOver(tempState);
								reference._showTooltip();
							};
							tick.addEventListener("mouseenter", enter, false);
							return enter;
						},
						addMouseLeave: function addMouseLeave(reference, tick) {
							var leave = function leave() {
								reference._hideTooltip();
							};
							tick.addEventListener("mouseleave", leave, false);
							return leave;
						}
					};
				},
				_layout: function _layout() {
					var this$1 = this;

					var positionPercentages;

					if (this.options.reversed) {
						positionPercentages = [100 - this._state.percentage[0], this.options.range ? 100 - this._state.percentage[1] : this._state.percentage[1]];
					} else {
						positionPercentages = [this._state.percentage[0], this._state.percentage[1]];
					}

					this.handle1.style[this.stylePos] = positionPercentages[0] + "%";
					this.handle1.setAttribute('aria-valuenow', this._state.value[0]);
					if (isNaN(this.options.formatter(this._state.value[0]))) {
						this.handle1.setAttribute('aria-valuetext', this.options.formatter(this._state.value[0]));
					}

					this.handle2.style[this.stylePos] = positionPercentages[1] + "%";
					this.handle2.setAttribute('aria-valuenow', this._state.value[1]);
					if (isNaN(this.options.formatter(this._state.value[1]))) {
						this.handle2.setAttribute('aria-valuetext', this.options.formatter(this._state.value[1]));
					}

					/* Position highlight range elements */
					if (this.rangeHighlightElements.length > 0 && Array.isArray(this.options.rangeHighlights) && this.options.rangeHighlights.length > 0) {
						for (var _i = 0; _i < this.options.rangeHighlights.length; _i++) {
							var startPercent = this$1._toPercentage(this$1.options.rangeHighlights[_i].start);
							var endPercent = this$1._toPercentage(this$1.options.rangeHighlights[_i].end);

							if (this$1.options.reversed) {
								var sp = 100 - endPercent;
								endPercent = 100 - startPercent;
								startPercent = sp;
							}

							var currentRange = this$1._createHighlightRange(startPercent, endPercent);

							if (currentRange) {
								if (this$1.options.orientation === 'vertical') {
									this$1.rangeHighlightElements[_i].style.top = currentRange.start + "%";
									this$1.rangeHighlightElements[_i].style.height = currentRange.size + "%";
								} else {
									if (this$1.options.rtl) {
										this$1.rangeHighlightElements[_i].style.right = currentRange.start + "%";
									} else {
										this$1.rangeHighlightElements[_i].style.left = currentRange.start + "%";
									}
									this$1.rangeHighlightElements[_i].style.width = currentRange.size + "%";
								}
							} else {
								this$1.rangeHighlightElements[_i].style.display = "none";
							}
						}
					}

					/* Position ticks and labels */
					if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) {

						var styleSize = this.options.orientation === 'vertical' ? 'height' : 'width';
						var styleMargin;
						if (this.options.orientation === 'vertical') {
							styleMargin = 'marginTop';
						} else {
							if (this.options.rtl) {
								styleMargin = 'marginRight';
							} else {
								styleMargin = 'marginLeft';
							}
						}
						var labelSize = this._state.size / (this.options.ticks.length - 1);

						if (this.tickLabelContainer) {
							var extraMargin = 0;
							if (this.options.ticks_positions.length === 0) {
								if (this.options.orientation !== 'vertical') {
									this.tickLabelContainer.style[styleMargin] = -labelSize / 2 + "px";
								}

								extraMargin = this.tickLabelContainer.offsetHeight;
							} else {
								/* Chidren are position absolute, calculate height by finding the max offsetHeight of a child */
								for (i = 0; i < this.tickLabelContainer.childNodes.length; i++) {
									if (this$1.tickLabelContainer.childNodes[i].offsetHeight > extraMargin) {
										extraMargin = this$1.tickLabelContainer.childNodes[i].offsetHeight;
									}
								}
							}
							if (this.options.orientation === 'horizontal') {
								this.sliderElem.style.marginBottom = extraMargin + "px";
							}
						}
						for (var i = 0; i < this.options.ticks.length; i++) {

							var percentage = this$1.options.ticks_positions[i] || this$1._toPercentage(this$1.options.ticks[i]);

							if (this$1.options.reversed) {
								percentage = 100 - percentage;
							}

							this$1.ticks[i].style[this$1.stylePos] = percentage + "%";

							/* Set class labels to denote whether ticks are in the selection */
							this$1._removeClass(this$1.ticks[i], 'in-selection');
							if (!this$1.options.range) {
								if (this$1.options.selection === 'after' && percentage >= positionPercentages[0]) {
									this$1._addClass(this$1.ticks[i], 'in-selection');
								} else if (this$1.options.selection === 'before' && percentage <= positionPercentages[0]) {
									this$1._addClass(this$1.ticks[i], 'in-selection');
								}
							} else if (percentage >= positionPercentages[0] && percentage <= positionPercentages[1]) {
								this$1._addClass(this$1.ticks[i], 'in-selection');
							}

							if (this$1.tickLabels[i]) {
								this$1.tickLabels[i].style[styleSize] = labelSize + "px";

								if (this$1.options.orientation !== 'vertical' && this$1.options.ticks_positions[i] !== undefined) {
									this$1.tickLabels[i].style.position = 'absolute';
									this$1.tickLabels[i].style[this$1.stylePos] = percentage + "%";
									this$1.tickLabels[i].style[styleMargin] = -labelSize / 2 + 'px';
								} else if (this$1.options.orientation === 'vertical') {
									if (this$1.options.rtl) {
										this$1.tickLabels[i].style['marginRight'] = this$1.sliderElem.offsetWidth + "px";
									} else {
										this$1.tickLabels[i].style['marginLeft'] = this$1.sliderElem.offsetWidth + "px";
									}
									this$1.tickLabelContainer.style[styleMargin] = this$1.sliderElem.offsetWidth / 2 * -1 + 'px';
								}
							}
						}
					}

					var formattedTooltipVal;

					if (this.options.range) {
						formattedTooltipVal = this.options.formatter(this._state.value);
						this._setText(this.tooltipInner, formattedTooltipVal);
						this.tooltip.style[this.stylePos] = (positionPercentages[1] + positionPercentages[0]) / 2 + "%";

						if (this.options.orientation === 'vertical') {
							this._css(this.tooltip, "margin-" + this.stylePos, -this.tooltip.offsetHeight / 2 + "px");
						} else {
							this._css(this.tooltip, "margin-" + this.stylePos, -this.tooltip.offsetWidth / 2 + "px");
						}

						var innerTooltipMinText = this.options.formatter(this._state.value[0]);
						this._setText(this.tooltipInner_min, innerTooltipMinText);

						var innerTooltipMaxText = this.options.formatter(this._state.value[1]);
						this._setText(this.tooltipInner_max, innerTooltipMaxText);

						this.tooltip_min.style[this.stylePos] = positionPercentages[0] + "%";

						if (this.options.orientation === 'vertical') {
							this._css(this.tooltip_min, "margin-" + this.stylePos, -this.tooltip_min.offsetHeight / 2 + "px");
						} else {
							this._css(this.tooltip_min, "margin-" + this.stylePos, -this.tooltip_min.offsetWidth / 2 + "px");
						}

						this.tooltip_max.style[this.stylePos] = positionPercentages[1] + "%";

						if (this.options.orientation === 'vertical') {
							this._css(this.tooltip_max, "margin-" + this.stylePos, -this.tooltip_max.offsetHeight / 2 + "px");
						} else {
							this._css(this.tooltip_max, "margin-" + this.stylePos, -this.tooltip_max.offsetWidth / 2 + "px");
						}
					} else {
						formattedTooltipVal = this.options.formatter(this._state.value[0]);
						this._setText(this.tooltipInner, formattedTooltipVal);

						this.tooltip.style[this.stylePos] = positionPercentages[0] + "%";
						if (this.options.orientation === 'vertical') {
							this._css(this.tooltip, "margin-" + this.stylePos, -this.tooltip.offsetHeight / 2 + "px");
						} else {
							this._css(this.tooltip, "margin-" + this.stylePos, -this.tooltip.offsetWidth / 2 + "px");
						}
					}

					if (this.options.orientation === 'vertical') {
						this.trackLow.style.top = '0';
						this.trackLow.style.height = Math.min(positionPercentages[0], positionPercentages[1]) + '%';

						this.trackSelection.style.top = Math.min(positionPercentages[0], positionPercentages[1]) + '%';
						this.trackSelection.style.height = Math.abs(positionPercentages[0] - positionPercentages[1]) + '%';

						this.trackHigh.style.bottom = '0';
						this.trackHigh.style.height = 100 - Math.min(positionPercentages[0], positionPercentages[1]) - Math.abs(positionPercentages[0] - positionPercentages[1]) + '%';
					} else {
						if (this.stylePos === 'right') {
							this.trackLow.style.right = '0';
						} else {
							this.trackLow.style.left = '0';
						}
						this.trackLow.style.width = Math.min(positionPercentages[0], positionPercentages[1]) + '%';

						if (this.stylePos === 'right') {
							this.trackSelection.style.right = Math.min(positionPercentages[0], positionPercentages[1]) + '%';
						} else {
							this.trackSelection.style.left = Math.min(positionPercentages[0], positionPercentages[1]) + '%';
						}
						this.trackSelection.style.width = Math.abs(positionPercentages[0] - positionPercentages[1]) + '%';

						if (this.stylePos === 'right') {
							this.trackHigh.style.left = '0';
						} else {
							this.trackHigh.style.right = '0';
						}
						this.trackHigh.style.width = 100 - Math.min(positionPercentages[0], positionPercentages[1]) - Math.abs(positionPercentages[0] - positionPercentages[1]) + '%';

						var offset_min = this.tooltip_min.getBoundingClientRect();
						var offset_max = this.tooltip_max.getBoundingClientRect();

						if (this.options.tooltip_position === 'bottom') {
							if (offset_min.right > offset_max.left) {
								this._removeClass(this.tooltip_max, 'bottom');
								this._addClass(this.tooltip_max, 'top');
								this.tooltip_max.style.top = '';
								this.tooltip_max.style.bottom = 22 + 'px';
							} else {
								this._removeClass(this.tooltip_max, 'top');
								this._addClass(this.tooltip_max, 'bottom');
								this.tooltip_max.style.top = this.tooltip_min.style.top;
								this.tooltip_max.style.bottom = '';
							}
						} else {
							if (offset_min.right > offset_max.left) {
								this._removeClass(this.tooltip_max, 'top');
								this._addClass(this.tooltip_max, 'bottom');
								this.tooltip_max.style.top = 18 + 'px';
							} else {
								this._removeClass(this.tooltip_max, 'bottom');
								this._addClass(this.tooltip_max, 'top');
								this.tooltip_max.style.top = this.tooltip_min.style.top;
							}
						}
					}
				},
				_createHighlightRange: function _createHighlightRange(start, end) {
					if (this._isHighlightRange(start, end)) {
						if (start > end) {
							return { 'start': end, 'size': start - end };
						}
						return { 'start': start, 'size': end - start };
					}
					return null;
				},
				_isHighlightRange: function _isHighlightRange(start, end) {
					if (0 <= start && start <= 100 && 0 <= end && end <= 100) {
						return true;
					} else {
						return false;
					}
				},
				_resize: function _resize(ev) {
					/*jshint unused:false*/
					this._state.offset = this._offset(this.sliderElem);
					this._state.size = this.sliderElem[this.sizePos];
					this._layout();
				},
				_removeProperty: function _removeProperty(element, prop) {
					if (element.style.removeProperty) {
						element.style.removeProperty(prop);
					} else {
						element.style.removeAttribute(prop);
					}
				},
				_mousedown: function _mousedown(ev) {
					if (!this._state.enabled) {
						return false;
					}

					this._state.offset = this._offset(this.sliderElem);
					this._state.size = this.sliderElem[this.sizePos];

					var percentage = this._getPercentage(ev);

					if (this.options.range) {
						var diff1 = Math.abs(this._state.percentage[0] - percentage);
						var diff2 = Math.abs(this._state.percentage[1] - percentage);
						this._state.dragged = diff1 < diff2 ? 0 : 1;
						this._adjustPercentageForRangeSliders(percentage);
					} else {
						this._state.dragged = 0;
					}

					this._state.percentage[this._state.dragged] = percentage;
					this._layout();

					if (this.touchCapable) {
						document.removeEventListener("touchmove", this.mousemove, false);
						document.removeEventListener("touchend", this.mouseup, false);
					}

					if (this.mousemove) {
						document.removeEventListener("mousemove", this.mousemove, false);
					}
					if (this.mouseup) {
						document.removeEventListener("mouseup", this.mouseup, false);
					}

					this.mousemove = this._mousemove.bind(this);
					this.mouseup = this._mouseup.bind(this);

					if (this.touchCapable) {
						// Touch: Bind touch events:
						document.addEventListener("touchmove", this.mousemove, false);
						document.addEventListener("touchend", this.mouseup, false);
					}
					// Bind mouse events:
					document.addEventListener("mousemove", this.mousemove, false);
					document.addEventListener("mouseup", this.mouseup, false);

					this._state.inDrag = true;
					var newValue = this._calculateValue();

					this._trigger('slideStart', newValue);

					this._setDataVal(newValue);
					this.setValue(newValue, false, true);

					ev.returnValue = false;

					if (this.options.focus) {
						this._triggerFocusOnHandle(this._state.dragged);
					}

					return true;
				},
				_touchstart: function _touchstart(ev) {
					if (ev.changedTouches === undefined) {
						this._mousedown(ev);
						return;
					}

					var touch = ev.changedTouches[0];
					this.touchX = touch.pageX;
					this.touchY = touch.pageY;
				},
				_triggerFocusOnHandle: function _triggerFocusOnHandle(handleIdx) {
					if (handleIdx === 0) {
						this.handle1.focus();
					}
					if (handleIdx === 1) {
						this.handle2.focus();
					}
				},
				_keydown: function _keydown(handleIdx, ev) {
					if (!this._state.enabled) {
						return false;
					}

					var dir;
					switch (ev.keyCode) {
						case 37: // left
						case 40:
							// down
							dir = -1;
							break;
						case 39: // right
						case 38:
							// up
							dir = 1;
							break;
					}
					if (!dir) {
						return;
					}

					// use natural arrow keys instead of from min to max
					if (this.options.natural_arrow_keys) {
						var ifVerticalAndNotReversed = this.options.orientation === 'vertical' && !this.options.reversed;
						var ifHorizontalAndReversed = this.options.orientation === 'horizontal' && this.options.reversed; // @todo control with rtl

						if (ifVerticalAndNotReversed || ifHorizontalAndReversed) {
							dir = -dir;
						}
					}

					var val = this._state.value[handleIdx] + dir * this.options.step;
					var percentage = val / this.options.max * 100;
					this._state.keyCtrl = handleIdx;
					if (this.options.range) {
						this._adjustPercentageForRangeSliders(percentage);
						var val1 = !this._state.keyCtrl ? val : this._state.value[0];
						var val2 = this._state.keyCtrl ? val : this._state.value[1];
						val = [val1, val2];
					}

					this._trigger('slideStart', val);
					this._setDataVal(val);
					this.setValue(val, true, true);

					this._setDataVal(val);
					this._trigger('slideStop', val);
					this._layout();

					this._pauseEvent(ev);
					delete this._state.keyCtrl;

					return false;
				},
				_pauseEvent: function _pauseEvent(ev) {
					if (ev.stopPropagation) {
						ev.stopPropagation();
					}
					if (ev.preventDefault) {
						ev.preventDefault();
					}
					ev.cancelBubble = true;
					ev.returnValue = false;
				},
				_mousemove: function _mousemove(ev) {
					if (!this._state.enabled) {
						return false;
					}

					var percentage = this._getPercentage(ev);
					this._adjustPercentageForRangeSliders(percentage);
					this._state.percentage[this._state.dragged] = percentage;
					this._layout();

					var val = this._calculateValue(true);
					this.setValue(val, true, true);

					return false;
				},
				_touchmove: function _touchmove(ev) {
					if (ev.changedTouches === undefined) {
						return;
					}

					var touch = ev.changedTouches[0];

					var xDiff = touch.pageX - this.touchX;
					var yDiff = touch.pageY - this.touchY;

					if (!this._state.inDrag) {
						// Vertical Slider
						if (this.options.orientation === 'vertical' && xDiff <= 5 && xDiff >= -5 && (yDiff >= 15 || yDiff <= -15)) {
							this._mousedown(ev);
						}
						// Horizontal slider.
						else if (yDiff <= 5 && yDiff >= -5 && (xDiff >= 15 || xDiff <= -15)) {
							this._mousedown(ev);
						}
					}
				},
				_adjustPercentageForRangeSliders: function _adjustPercentageForRangeSliders(percentage) {
					if (this.options.range) {
						var precision = this._getNumDigitsAfterDecimalPlace(percentage);
						precision = precision ? precision - 1 : 0;
						var percentageWithAdjustedPrecision = this._applyToFixedAndParseFloat(percentage, precision);
						if (this._state.dragged === 0 && this._applyToFixedAndParseFloat(this._state.percentage[1], precision) < percentageWithAdjustedPrecision) {
							this._state.percentage[0] = this._state.percentage[1];
							this._state.dragged = 1;
						} else if (this._state.dragged === 1 && this._applyToFixedAndParseFloat(this._state.percentage[0], precision) > percentageWithAdjustedPrecision) {
							this._state.percentage[1] = this._state.percentage[0];
							this._state.dragged = 0;
						} else if (this._state.keyCtrl === 0 && this._state.value[1] / this.options.max * 100 < percentage) {
							this._state.percentage[0] = this._state.percentage[1];
							this._state.keyCtrl = 1;
							this.handle2.focus();
						} else if (this._state.keyCtrl === 1 && this._state.value[0] / this.options.max * 100 > percentage) {
							this._state.percentage[1] = this._state.percentage[0];
							this._state.keyCtrl = 0;
							this.handle1.focus();
						}
					}
				},
				_mouseup: function _mouseup() {
					if (!this._state.enabled) {
						return false;
					}
					if (this.touchCapable) {
						// Touch: Unbind touch event handlers:
						document.removeEventListener("touchmove", this.mousemove, false);
						document.removeEventListener("touchend", this.mouseup, false);
					}
					// Unbind mouse event handlers:
					document.removeEventListener("mousemove", this.mousemove, false);
					document.removeEventListener("mouseup", this.mouseup, false);

					this._state.inDrag = false;
					if (this._state.over === false) {
						this._hideTooltip();
					}
					var val = this._calculateValue(true);

					this._layout();
					this._setDataVal(val);
					this._trigger('slideStop', val);

					return false;
				},
				_calculateValue: function _calculateValue(snapToClosestTick) {
					var this$1 = this;

					var val;
					if (this.options.range) {
						val = [this.options.min, this.options.max];
						if (this._state.percentage[0] !== 0) {
							val[0] = this._toValue(this._state.percentage[0]);
							val[0] = this._applyPrecision(val[0]);
						}
						if (this._state.percentage[1] !== 100) {
							val[1] = this._toValue(this._state.percentage[1]);
							val[1] = this._applyPrecision(val[1]);
						}
					} else {
						val = this._toValue(this._state.percentage[0]);
						val = parseFloat(val);
						val = this._applyPrecision(val);
					}

					if (snapToClosestTick) {
						var min = [val, Infinity];
						for (var i = 0; i < this.options.ticks.length; i++) {
							var diff = Math.abs(this$1.options.ticks[i] - val);
							if (diff <= min[1]) {
								min = [this$1.options.ticks[i], diff];
							}
						}
						if (min[1] <= this.options.ticks_snap_bounds) {
							return min[0];
						}
					}

					return val;
				},
				_applyPrecision: function _applyPrecision(val) {
					var precision = this.options.precision || this._getNumDigitsAfterDecimalPlace(this.options.step);
					return this._applyToFixedAndParseFloat(val, precision);
				},
				_getNumDigitsAfterDecimalPlace: function _getNumDigitsAfterDecimalPlace(num) {
					var match = ('' + num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
					if (!match) {
						return 0;
					}
					return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
				},
				_applyToFixedAndParseFloat: function _applyToFixedAndParseFloat(num, toFixedInput) {
					var truncatedNum = num.toFixed(toFixedInput);
					return parseFloat(truncatedNum);
				},
				/*
			  Credits to Mike Samuel for the following method!
			  Source: http://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number
	   */
				_getPercentage: function _getPercentage(ev) {
					if (this.touchCapable && (ev.type === 'touchstart' || ev.type === 'touchmove')) {
						ev = ev.touches[0];
					}

					var eventPosition = ev[this.mousePos];
					var sliderOffset = this._state.offset[this.stylePos];
					var distanceToSlide = eventPosition - sliderOffset;
					if (this.stylePos === 'right') {
						distanceToSlide = -distanceToSlide;
					}
					// Calculate what percent of the length the slider handle has slid
					var percentage = distanceToSlide / this._state.size * 100;
					percentage = Math.round(percentage / this._state.percentage[2]) * this._state.percentage[2];
					if (this.options.reversed) {
						percentage = 100 - percentage;
					}

					// Make sure the percent is within the bounds of the slider.
					// 0% corresponds to the 'min' value of the slide
					// 100% corresponds to the 'max' value of the slide
					return Math.max(0, Math.min(100, percentage));
				},
				_validateInputValue: function _validateInputValue(val) {
					if (!isNaN(+val)) {
						return +val;
					} else if (Array.isArray(val)) {
						this._validateArray(val);
						return val;
					} else {
						throw new Error(ErrorMsgs.formatInvalidInputErrorMsg(val));
					}
				},
				_validateArray: function _validateArray(val) {
					for (var i = 0; i < val.length; i++) {
						var input = val[i];
						if (typeof input !== 'number') {
							throw new Error(ErrorMsgs.formatInvalidInputErrorMsg(input));
						}
					}
				},
				_setDataVal: function _setDataVal(val) {
					this.element.setAttribute('data-value', val);
					this.element.setAttribute('value', val);
					this.element.value = val;
				},
				_trigger: function _trigger(evt, val) {
					val = val || val === 0 ? val : undefined;

					var callbackFnArray = this.eventToCallbackMap[evt];
					if (callbackFnArray && callbackFnArray.length) {
						for (var i = 0; i < callbackFnArray.length; i++) {
							var callbackFn = callbackFnArray[i];
							callbackFn(val);
						}
					}

					/* If JQuery exists, trigger JQuery events */
					if ($) {
						this._triggerJQueryEvent(evt, val);
					}
				},
				_triggerJQueryEvent: function _triggerJQueryEvent(evt, val) {
					var eventData = {
						type: evt,
						value: val
					};
					this.$element.trigger(eventData);
					this.$sliderElem.trigger(eventData);
				},
				_unbindJQueryEventHandlers: function _unbindJQueryEventHandlers() {
					this.$element.off();
					this.$sliderElem.off();
				},
				_setText: function _setText(element, text) {
					if (typeof element.textContent !== "undefined") {
						element.textContent = text;
					} else if (typeof element.innerText !== "undefined") {
						element.innerText = text;
					}
				},
				_removeClass: function _removeClass(element, classString) {
					var classes = classString.split(" ");
					var newClasses = element.className;

					for (var i = 0; i < classes.length; i++) {
						var classTag = classes[i];
						var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)");
						newClasses = newClasses.replace(regex, " ");
					}

					element.className = newClasses.trim();
				},
				_addClass: function _addClass(element, classString) {
					var classes = classString.split(" ");
					var newClasses = element.className;

					for (var i = 0; i < classes.length; i++) {
						var classTag = classes[i];
						var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)");
						var ifClassExists = regex.test(newClasses);

						if (!ifClassExists) {
							newClasses += " " + classTag;
						}
					}

					element.className = newClasses.trim();
				},
				_offsetLeft: function _offsetLeft(obj) {
					return obj.getBoundingClientRect().left;
				},
				_offsetRight: function _offsetRight(obj) {
					return obj.getBoundingClientRect().right;
				},
				_offsetTop: function _offsetTop(obj) {
					var offsetTop = obj.offsetTop;
					while ((obj = obj.offsetParent) && !isNaN(obj.offsetTop)) {
						offsetTop += obj.offsetTop;
						if (obj.tagName !== 'BODY') {
							offsetTop -= obj.scrollTop;
						}
					}
					return offsetTop;
				},
				_offset: function _offset(obj) {
					return {
						left: this._offsetLeft(obj),
						right: this._offsetRight(obj),
						top: this._offsetTop(obj)
					};
				},
				_css: function _css(elementRef, styleName, value) {
					if ($) {
						$.style(elementRef, styleName, value);
					} else {
						var style = styleName.replace(/^-ms-/, "ms-").replace(/-([\da-z])/gi, function (all, letter) {
							return letter.toUpperCase();
						});
						elementRef.style[style] = value;
					}
				},
				_toValue: function _toValue(percentage) {
					return this.options.scale.toValue.apply(this, [percentage]);
				},
				_toPercentage: function _toPercentage(value) {
					return this.options.scale.toPercentage.apply(this, [value]);
				},
				_setTooltipPosition: function _setTooltipPosition() {
					var tooltips = [this.tooltip, this.tooltip_min, this.tooltip_max];
					if (this.options.orientation === 'vertical') {
						var tooltipPos;
						if (this.options.tooltip_position) {
							tooltipPos = this.options.tooltip_position;
						} else {
							if (this.options.rtl) {
								tooltipPos = 'left';
							} else {
								tooltipPos = 'right';
							}
						}
						var oppositeSide = tooltipPos === 'left' ? 'right' : 'left';
						tooltips.forEach(function (tooltip) {
							this._addClass(tooltip, tooltipPos);
							tooltip.style[oppositeSide] = '100%';
						}.bind(this));
					} else if (this.options.tooltip_position === 'bottom') {
						tooltips.forEach(function (tooltip) {
							this._addClass(tooltip, 'bottom');
							tooltip.style.top = 22 + 'px';
						}.bind(this));
					} else {
						tooltips.forEach(function (tooltip) {
							this._addClass(tooltip, 'top');
							tooltip.style.top = -this.tooltip.outerHeight - 14 + 'px';
						}.bind(this));
					}
				}
			};

			/*********************************
				Attach to global namespace
			*********************************/
			if ($ && $.fn) {
				var autoRegisterNamespace = void 0;

				if (!$.fn.slider) {
					$.bridget(NAMESPACE_MAIN, Slider);
					autoRegisterNamespace = NAMESPACE_MAIN;
				} else {
					if (windowIsDefined) {
						window.console.warn("bootstrap-slider.js - WARNING: $.fn.slider namespace is already bound. Use the $.fn.bootstrapSlider namespace instead.");
					}
					autoRegisterNamespace = NAMESPACE_ALTERNATE;
				}
				$.bridget(NAMESPACE_ALTERNATE, Slider);

				// Auto-Register data-provide="slider" Elements
				$(function () {
					$("input[data-provide=slider]")[autoRegisterNamespace]();
				});
			}
		})($);

		return Slider;
	});
});

//
function setState(state) {
	SPKManage.setStateJson(JSON.stringify(state));
}

function getState() {
	return JSON.parse(SPKManage.getStateJson())
}

var SRState = {
	start_page_name: 'Login',
	login_id: null,
	curTestSetId: null,
	curPageName: null,
	volume: 70,
	rec_volume: 75,
	skip_rec_check_desc: false,
	prev_page_name: '',
	prev_page_visited: 0,
	apply_id: null,
	year: null,
	kaiji: null,
	yes_no_page_map: new Object(),
	replay_max_click_count: 0,
	current_temp_rec_file_name: '',
	reply_button_clicked: false,
	popup_show_flag: 0,
	mouse_click_flag: 1,
	is_yes_no_type_page: 0,
	reload_count: 0,
};

var wrap_log = function (func) {
	return function () {
		var param = [], len = arguments.length;
		while (len--) param[len] = arguments[len];

		try {
			return func.apply(this, param)
		} catch (e) {
			pauseNativeVideoPlayback();
			showSpkBodyModal('error');
			jquery$1('#exitButtonDiv').hide();
			jquery$1('#errorCode').text(jquery$1('#errorCode').text() + ` ${e.message}`);
			if ((jquery$1('#errorCode').text()).includes('マイクが接続されていません')) {
				jquery$1('#errorCode').text('マイクが接続されていません');
				jquery$1('#errorCode').text('マイクが接続されていません. Please call the administrator and then administrator will close the application.');
				//jquery$1('#altErrSpan').innerHTML ='RELOAD';

				//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- START
				jquery$1('#reloadButtonDiv').hide();
				jquery$1('#exitButtonDiv').hide();
				jquery$1('#altErr').hide();

				//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- END
			}
			//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- START
			if ((jquery$1('#errorCode').text()).includes('ErrorInRecProgress')) {
				jquery$1('#errorCode').text('Error occurred in audio recording. Please call the administrator and then administrator will close the application.');
				//jquery$1('#altExitSpan').innerHTML ='EXIT';
				if (SRState.reload_count > 3) {
					jquery$1('#reloadButtonDiv').hide();
					SPKManage.writeLog('ErrorInRecProgress reloadButton Hide, reload count [' + SRState.reload_count + ']');
				} else {
					SPKManage.writeLog('ErrorInRecProgress reloadButton Disp, reload count [' + SRState.reload_count + ']');
					SRState.reload_count = SRState.reload_count + 1;
				}
				jquery$1('#exitButtonDiv').hide();
			}
			//Added By Sharad EIK#235 Hide Reload button -- Start
			if ((jquery$1('#errorCode').text()).includes('Interview id not Exist')) {
				jquery$1('#errorCode').text('Interview id not Exist.');
				//jquery$1('#altExitSpan').innerHTML ='EXIT';
				jquery$1('#reloadButtonDiv').hide();
				jquery$1('#exitButtonDiv').hide();
			}
			//Added By Sharad EIK#235 Hide Reload button -- End
			//Added By Sharad EIK#284 Mic Mute Error Message -- Start
			if ((jquery$1('#errorCode').text()).includes('マイクがミュートになっています。')) {
				jquery$1('#errorCode').text('マイクがミュートになっています。 Please call the administrator and then administrator will close the application.');
				jquery$1('#reloadButtonDiv').hide();
				jquery$1('#exitButtonDiv').hide();
			}
			//Added By Sharad EIK#284 Mic Mute Error Message -- End
			jquery$1('#altExit').on('click', function () {
				jquery$1('audio,video').off('abort emptied').each(function (i, elem) {
					elem.pause();
					elem.removeAttribute('src');
					elem.load();
				});

				SPKManage.stopRecordSound(100);
				SPKManage.stopMicCheck();
				SPKManage.delTempFile();

				SPKManage.exitApplication();
			});
			//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- END
			jquery$1('#altErr').on('click', function () {
				if (window['pageManager'] && SRState.curPageName && !jquery$1('#errorCode').text().includes('FileMergeError')) {
					SPKManage.writeLog(`reload page: ${SRState.curPageName}`);
					if (jquery$1('#errorCode').text().includes('マイクが接続されていません')) {
						//IF MicMute Error Then before reload we will move all temp recording file to applog
						if (SRState.current_temp_rec_file_name != '' && SRState.current_temp_rec_file_name != null) {
							SPKManage.moveTempFileOnReload(SRState.current_temp_rec_file_name);
						}
						SRState.current_temp_rec_file_name = '';
					}
					SRState.prev_page_visited = 1;
					SPKManage.normalizeTempFiles(SRState.login_id);
					pageManager.showPage(SRState.curPageName);
				}
				return false;
			});

			SPKManage.writeLog(`${e.message}: ${e.line}: ${e.column}: ${e.sourceURL}`);
			alert(`${e.message}: ${e.line}: ${e.column}: ${e.sourceURL}`);
			if (e.stack) {
				SPKManage.writeLog(e.stack);
				alert(e.stack);
			}
			throw e
		}
	}
};

var terminated = false;

function TerminateTimers() {
	SPKManage.writeLog(`CountDownTimer: terminated`);
	terminated = true;
}

class CountDownTimerBase {
	constructor() {
		this._on_start = null;
		this._on_tick = null;
		this._on_stop = null;
		this._on_pause = null;
		this._on_resume = null;
		this._log_second = null;
	}

	start() {
		var this$1 = this;

		if (this._on_start && !terminated) {
			this._on_start(this);
		}

		var tick = function () {
			if (this$1._on_tick && !terminated) {

				if (this$1._log_second != this$1.getSecond()) {
					this$1._log_second = this$1.getSecond();
					SPKManage.writeLog(`CountDownTimer: ${this$1._log_second}`);
				}
				if (this$1._on_tick(this$1) !== false) {
					setTimeout(wrap_log(tick), 100);
				}
			}
		};
		tick();
	}

	stop() {
		if (this._on_stop && !terminated) {
			this._on_stop(this);
		}
	}

	pause() {
		if (this._on_pause) {
			this._on_pause(this);
		}
	}

	resume() {
		if (this._on_resume) {
			this._on_resume(this);
		}
	}

	isResumable() {
		return true
	}

	onStart(proc) {
		this._on_start = proc;
	}
	onTick(proc) {
		this._on_tick = proc;
	}
	onStop(proc) {
		this._on_stop = proc;
	}

	onPause(proc) {
		this._on_pause = proc;
	}
	onResume(proc) {
		this._on_resume = proc;
	}

	text() {
		var sec = this.getSecond();
		var min_str = ('00' + Math.floor(sec / 60)).slice(-2);
		var sec_str = ('00' + sec % 60).slice(-2);

		return `${min_str} : ${sec_str}`
	}
}

// カウントダウンタイマー
class CountDownTimer extends CountDownTimerBase {
	constructor(duration) {
		super();
		this.duration = duration;
		this.startTime = null;
		this.paused_time_ms = 0;
	}

	start() {
		var now = new Date().getTime();
		this.startTime = now - this.paused_time_ms;
		this.paused_time_ms = 0;
		super.start();
	}

	pause() {
		super.pause();
		if (this.startTime === null) {
			return
		}

		var now = new Date().getTime();
		this.paused_time_ms = now - this.startTime;
		var temp = this.paused_time_ms;
	}

	resume() {
		super.resume();
		if (this.startTime !== null) {
			var now = new Date().getTime();
			this.startTime = now - this.paused_time_ms;
			this.paused_time_ms = 0;
		}
	}

	stop() {
		super.stop();
		this.startTime = null;
	}

	getSecond() {
		if (this.startTime == null) {
			return this.duration
		}

		var now = new Date().getTime();
		var ms = this.paused_time_ms ? this.paused_time_ms : now - this.startTime;
		var s = this.duration - Math.floor(ms / 1000);
		/*SPKManage.writeLog(ms);
		SPKManage.writeLog(s);*/
		return s > 0 ? s : 0
	}

	isStarted() {
		return this.startTime != null;
	}
}

// 録音タイマー
class RecordTimer extends CountDownTimerBase {
	constructor(duration) {
		super();
		this.duration = duration;
		this.started = false;
	}

	start() {
		if (!SPKManage.checkMicAvalable()) {
			throw new Error('マイクが接続されていません')
		}
		SPKManage.startRecordSound(this.duration);
		this.started = true;

		super.start();
	}

	pause() {
		/*SPKManage.stopRecordSound(SRState.rec_volume);
		super.pause();*/
		this.started = false;
	}

	resume() {
		super.resume();
	}

	isResumable() {
		return false
	}

	stop() {
		SPKManage.stopRecordSound(100);
		this.started = false;
		super.stop();
	}

	getSecond() {
		return this.duration - Math.floor(this.duration * SPKManage.getRecordingProgress())
	}

	isStarted() {
		return this.started
	}
}

var mustache = createCommonjsModule(function (module, exports) {
	/*!
	 * mustache.js - Logic-less {{mustache}} templates with JavaScript
	 * http://github.com/janl/mustache.js
	 */

	/*global define: false Mustache: true*/

	(function defineMustache(global, factory) {
		if ('object' === 'object' && exports && typeof exports.nodeName !== 'string') {
			factory(exports); // CommonJS
		} else if (typeof undefined === 'function' && undefined.amd) {
			undefined(['exports'], factory); // AMD
		} else {
			global.Mustache = {};
			factory(global.Mustache); // script, wsh, asp
		}
	}(commonjsGlobal, function mustacheFactory(mustache) {

		var objectToString = Object.prototype.toString;
		var isArray = Array.isArray || function isArrayPolyfill(object) {
			return objectToString.call(object) === '[object Array]';
		};

		function isFunction(object) {
			return typeof object === 'function';
		}

		/**
		 * More correct typeof string handling array
		 * which normally returns typeof 'object'
		 */
		function typeStr(obj) {
			return isArray(obj) ? 'array' : typeof obj;
		}

		function escapeRegExp(string) {
			return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&');
		}

		/**
		 * Null safe way of checking whether or not an object,
		 * including its prototype, has a given property
		 */
		function hasProperty(obj, propName) {
			return obj != null && typeof obj === 'object' && (propName in obj);
		}

		// Workaround for https://issues.apache.org/jira/browse/COUCHDB-577
		// See https://github.com/janl/mustache.js/issues/189
		var regExpTest = RegExp.prototype.test;
		function testRegExp(re, string) {
			return regExpTest.call(re, string);
		}

		var nonSpaceRe = /\S/;
		function isWhitespace(string) {
			return !testRegExp(nonSpaceRe, string);
		}

		var entityMap = {
			'&': '&amp;',
			'<': '&lt;',
			'>': '&gt;',
			'"': '&quot;',
			"'": '&#39;',
			'/': '&#x2F;',
			'`': '&#x60;',
			'=': '&#x3D;'
		};

		function escapeHtml(string) {
			return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap(s) {
				return entityMap[s];
			});
		}

		var whiteRe = /\s*/;
		var spaceRe = /\s+/;
		var equalsRe = /\s*=/;
		var curlyRe = /\s*\}/;
		var tagRe = /#|\^|\/|>|\{|&|=|!/;

		/**
		 * Breaks up the given `template` string into a tree of tokens. If the `tags`
		 * argument is given here it must be an array with two string values: the
		 * opening and closing tags used in the template (e.g. [ "<%", "%>" ]). Of
		 * course, the default is to use mustaches (i.e. mustache.tags).
		 *
		 * A token is an array with at least 4 elements. The first element is the
		 * mustache symbol that was used inside the tag, e.g. "#" or "&". If the tag
		 * did not contain a symbol (i.e. {{myValue}}) this element is "name". For
		 * all text that appears outside a symbol this element is "text".
		 *
		 * The second element of a token is its "value". For mustache tags this is
		 * whatever else was inside the tag besides the opening symbol. For text tokens
		 * this is the text itself.
		 *
		 * The third and fourth elements of the token are the start and end indices,
		 * respectively, of the token in the original template.
		 *
		 * Tokens that are the root node of a subtree contain two more elements: 1) an
		 * array of tokens in the subtree and 2) the index in the original template at
		 * which the closing tag for that section begins.
		 */
		function parseTemplate(template, tags) {
			if (!template) { return []; }

			var sections = [];     // Stack to hold section tokens
			var tokens = [];       // Buffer to hold the tokens
			var spaces = [];       // Indices of whitespace tokens on the current line
			var hasTag = false;    // Is there a {{tag}} on the current line?
			var nonSpace = false;  // Is there a non-space char on the current line?

			// Strips all whitespace tokens array for the current line
			// if there was a {{#tag}} on it and otherwise only space.
			function stripSpace() {
				if (hasTag && !nonSpace) {
					while (spaces.length) { delete tokens[spaces.pop()]; }
				} else {
					spaces = [];
				}

				hasTag = false;
				nonSpace = false;
			}

			var openingTagRe, closingTagRe, closingCurlyRe;
			function compileTags(tagsToCompile) {
				if (typeof tagsToCompile === 'string') { tagsToCompile = tagsToCompile.split(spaceRe, 2); }

				if (!isArray(tagsToCompile) || tagsToCompile.length !== 2) { throw new Error('Invalid tags: ' + tagsToCompile); }

				openingTagRe = new RegExp(escapeRegExp(tagsToCompile[0]) + '\\s*');
				closingTagRe = new RegExp('\\s*' + escapeRegExp(tagsToCompile[1]));
				closingCurlyRe = new RegExp('\\s*' + escapeRegExp('}' + tagsToCompile[1]));
			}

			compileTags(tags || mustache.tags);

			var scanner = new Scanner(template);

			var start, type, value, chr, token, openSection;
			while (!scanner.eos()) {
				start = scanner.pos;

				// Match any text between tags.
				value = scanner.scanUntil(openingTagRe);

				if (value) {
					for (var i = 0, valueLength = value.length; i < valueLength; ++i) {
						chr = value.charAt(i);

						if (isWhitespace(chr)) {
							spaces.push(tokens.length);
						} else {
							nonSpace = true;
						}

						tokens.push(['text', chr, start, start + 1]);
						start += 1;

						// Check for whitespace on the current line.
						if (chr === '\n') { stripSpace(); }
					}
				}

				// Match the opening tag.
				if (!scanner.scan(openingTagRe)) { break; }

				hasTag = true;

				// Get the tag type.
				type = scanner.scan(tagRe) || 'name';
				scanner.scan(whiteRe);

				// Get the tag value.
				if (type === '=') {
					value = scanner.scanUntil(equalsRe);
					scanner.scan(equalsRe);
					scanner.scanUntil(closingTagRe);
				} else if (type === '{') {
					value = scanner.scanUntil(closingCurlyRe);
					scanner.scan(curlyRe);
					scanner.scanUntil(closingTagRe);
					type = '&';
				} else {
					value = scanner.scanUntil(closingTagRe);
				}

				// Match the closing tag.
				if (!scanner.scan(closingTagRe)) { throw new Error('Unclosed tag at ' + scanner.pos); }

				token = [type, value, start, scanner.pos];
				tokens.push(token);

				if (type === '#' || type === '^') {
					sections.push(token);
				} else if (type === '/') {
					// Check section nesting.
					openSection = sections.pop();

					if (!openSection) { throw new Error('Unopened section "' + value + '" at ' + start); }

					if (openSection[1] !== value) { throw new Error('Unclosed section "' + openSection[1] + '" at ' + start); }
				} else if (type === 'name' || type === '{' || type === '&') {
					nonSpace = true;
				} else if (type === '=') {
					// Set the tags for the next time around.
					compileTags(value);
				}
			}

			// Make sure there are no open sections when we're done.
			openSection = sections.pop();

			if (openSection) { throw new Error('Unclosed section "' + openSection[1] + '" at ' + scanner.pos); }

			return nestTokens(squashTokens(tokens));
		}

		/**
		 * Combines the values of consecutive text tokens in the given `tokens` array
		 * to a single token.
		 */
		function squashTokens(tokens) {
			var squashedTokens = [];

			var token, lastToken;
			for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
				token = tokens[i];

				if (token) {
					if (token[0] === 'text' && lastToken && lastToken[0] === 'text') {
						lastToken[1] += token[1];
						lastToken[3] = token[3];
					} else {
						squashedTokens.push(token);
						lastToken = token;
					}
				}
			}

			return squashedTokens;
		}

		/**
		 * Forms the given array of `tokens` into a nested tree structure where
		 * tokens that represent a section have two additional items: 1) an array of
		 * all tokens that appear in that section and 2) the index in the original
		 * template that represents the end of that section.
		 */
		function nestTokens(tokens) {
			var nestedTokens = [];
			var collector = nestedTokens;
			var sections = [];

			var token, section;
			for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
				token = tokens[i];

				switch (token[0]) {
					case '#':
					case '^':
						collector.push(token);
						sections.push(token);
						collector = token[4] = [];
						break;
					case '/':
						section = sections.pop();
						section[5] = token[2];
						collector = sections.length > 0 ? sections[sections.length - 1][4] : nestedTokens;
						break;
					default:
						collector.push(token);
				}
			}

			return nestedTokens;
		}

		/**
		 * A simple string scanner that is used by the template parser to find
		 * tokens in template strings.
		 */
		function Scanner(string) {
			this.string = string;
			this.tail = string;
			this.pos = 0;
		}

		/**
		 * Returns `true` if the tail is empty (end of string).
		 */
		Scanner.prototype.eos = function eos() {
			return this.tail === '';
		};

		/**
		 * Tries to match the given regular expression at the current position.
		 * Returns the matched text if it can match, the empty string otherwise.
		 */
		Scanner.prototype.scan = function scan(re) {
			var match = this.tail.match(re);

			if (!match || match.index !== 0) { return ''; }

			var string = match[0];

			this.tail = this.tail.substring(string.length);
			this.pos += string.length;

			return string;
		};

		/**
		 * Skips all text until the given regular expression can be matched. Returns
		 * the skipped string, which is the entire tail if no match can be made.
		 */
		Scanner.prototype.scanUntil = function scanUntil(re) {
			var index = this.tail.search(re), match;

			switch (index) {
				case -1:
					match = this.tail;
					this.tail = '';
					break;
				case 0:
					match = '';
					break;
				default:
					match = this.tail.substring(0, index);
					this.tail = this.tail.substring(index);
			}

			this.pos += match.length;

			return match;
		};

		/**
		 * Represents a rendering context by wrapping a view object and
		 * maintaining a reference to the parent context.
		 */
		function Context(view, parentContext) {
			this.view = view;
			this.cache = { '.': this.view };
			this.parent = parentContext;
		}

		/**
		 * Creates a new context using the given view with this context
		 * as the parent.
		 */
		Context.prototype.push = function push(view) {
			return new Context(view, this);
		};

		/**
		 * Returns the value of the given name in this context, traversing
		 * up the context hierarchy if the value is absent in this context's view.
		 */
		Context.prototype.lookup = function lookup(name) {
			var cache = this.cache;

			var value;
			if (cache.hasOwnProperty(name)) {
				value = cache[name];
			} else {
				var context = this, names, index, lookupHit = false;

				while (context) {
					if (name.indexOf('.') > 0) {
						value = context.view;
						names = name.split('.');
						index = 0;

						/**
						 * Using the dot notion path in `name`, we descend through the
						 * nested objects.
						 *
						 * To be certain that the lookup has been successful, we have to
						 * check if the last object in the path actually has the property
						 * we are looking for. We store the result in `lookupHit`.
						 *
						 * This is specially necessary for when the value has been set to
						 * `undefined` and we want to avoid looking up parent contexts.
						 **/
						while (value != null && index < names.length) {
							if (index === names.length - 1) { lookupHit = hasProperty(value, names[index]); }

							value = value[names[index++]];
						}
					} else {
						value = context.view[name];
						lookupHit = hasProperty(context.view, name);
					}

					if (lookupHit) { break; }

					context = context.parent;
				}

				cache[name] = value;
			}

			if (isFunction(value)) { value = value.call(this.view); }

			return value;
		};

		/**
		 * A Writer knows how to take a stream of tokens and render them to a
		 * string, given a context. It also maintains a cache of templates to
		 * avoid the need to parse the same template twice.
		 */
		function Writer() {
			this.cache = {};
		}

		/**
		 * Clears all cached templates in this writer.
		 */
		Writer.prototype.clearCache = function clearCache() {
			this.cache = {};
		};

		/**
		 * Parses and caches the given `template` and returns the array of tokens
		 * that is generated from the parse.
		 */
		Writer.prototype.parse = function parse(template, tags) {
			var cache = this.cache;
			var tokens = cache[template];

			if (tokens == null) { tokens = cache[template] = parseTemplate(template, tags); }

			return tokens;
		};

		/**
		 * High-level method that is used to render the given `template` with
		 * the given `view`.
		 *
		 * The optional `partials` argument may be an object that contains the
		 * names and templates of partials that are used in the template. It may
		 * also be a function that is used to load partial templates on the fly
		 * that takes a single argument: the name of the partial.
		 */
		Writer.prototype.render = function render(template, view, partials) {
			var tokens = this.parse(template);
			var context = (view instanceof Context) ? view : new Context(view);
			return this.renderTokens(tokens, context, partials, template);
		};

		/**
		 * Low-level method that renders the given array of `tokens` using
		 * the given `context` and `partials`.
		 *
		 * Note: The `originalTemplate` is only ever used to extract the portion
		 * of the original template that was contained in a higher-order section.
		 * If the template doesn't use higher-order sections, this argument may
		 * be omitted.
		 */
		Writer.prototype.renderTokens = function renderTokens(tokens, context, partials, originalTemplate) {
			var this$1 = this;

			var buffer = '';

			var token, symbol, value;
			for (var i = 0, numTokens = tokens.length; i < numTokens; ++i) {
				value = undefined;
				token = tokens[i];
				symbol = token[0];

				if (symbol === '#') { value = this$1.renderSection(token, context, partials, originalTemplate); }
				else if (symbol === '^') { value = this$1.renderInverted(token, context, partials, originalTemplate); }
				else if (symbol === '>') { value = this$1.renderPartial(token, context, partials, originalTemplate); }
				else if (symbol === '&') { value = this$1.unescapedValue(token, context); }
				else if (symbol === 'name') { value = this$1.escapedValue(token, context); }
				else if (symbol === 'text') { value = this$1.rawValue(token); }

				if (value !== undefined) { buffer += value; }
			}

			return buffer;
		};

		Writer.prototype.renderSection = function renderSection(token, context, partials, originalTemplate) {
			var this$1 = this;

			var self = this;
			var buffer = '';
			var value = context.lookup(token[1]);

			// This function is used to render an arbitrary template
			// in the current context by higher-order sections.
			function subRender(template) {
				return self.render(template, context, partials);
			}

			if (!value) { return; }

			if (isArray(value)) {
				for (var j = 0, valueLength = value.length; j < valueLength; ++j) {
					buffer += this$1.renderTokens(token[4], context.push(value[j]), partials, originalTemplate);
				}
			} else if (typeof value === 'object' || typeof value === 'string' || typeof value === 'number') {
				buffer += this.renderTokens(token[4], context.push(value), partials, originalTemplate);
			} else if (isFunction(value)) {
				if (typeof originalTemplate !== 'string') { throw new Error('Cannot use higher-order sections without the original template'); }

				// Extract the portion of the original template that the section contains.
				value = value.call(context.view, originalTemplate.slice(token[3], token[5]), subRender);

				if (value != null) { buffer += value; }
			} else {
				buffer += this.renderTokens(token[4], context, partials, originalTemplate);
			}
			return buffer;
		};

		Writer.prototype.renderInverted = function renderInverted(token, context, partials, originalTemplate) {
			var value = context.lookup(token[1]);

			// Use JavaScript's definition of falsy. Include empty arrays.
			// See https://github.com/janl/mustache.js/issues/186
			if (!value || (isArray(value) && value.length === 0)) { return this.renderTokens(token[4], context, partials, originalTemplate); }
		};

		Writer.prototype.renderPartial = function renderPartial(token, context, partials) {
			if (!partials) { return; }

			var value = isFunction(partials) ? partials(token[1]) : partials[token[1]];
			if (value != null) { return this.renderTokens(this.parse(value), context, partials, value); }
		};

		Writer.prototype.unescapedValue = function unescapedValue(token, context) {
			var value = context.lookup(token[1]);
			if (value != null) { return value; }
		};

		Writer.prototype.escapedValue = function escapedValue(token, context) {
			var value = context.lookup(token[1]);
			if (value != null) { return mustache.escape(value); }
		};

		Writer.prototype.rawValue = function rawValue(token) {
			return token[1];
		};

		mustache.name = 'mustache.js';
		mustache.version = '2.3.0';
		mustache.tags = ['{{', '}}'];

		// All high-level mustache.* functions use this writer.
		var defaultWriter = new Writer();

		/**
		 * Clears all cached templates in the default writer.
		 */
		mustache.clearCache = function clearCache() {
			return defaultWriter.clearCache();
		};

		/**
		 * Parses and caches the given template in the default writer and returns the
		 * array of tokens it contains. Doing this ahead of time avoids the need to
		 * parse templates on the fly as they are rendered.
		 */
		mustache.parse = function parse(template, tags) {
			return defaultWriter.parse(template, tags);
		};

		/**
		 * Renders the `template` with the given `view` and `partials` using the
		 * default writer.
		 */
		mustache.render = function render(template, view, partials) {
			if (typeof template !== 'string') {
				throw new TypeError('Invalid template! Template should be a "string" ' +
					'but "' + typeStr(template) + '" was given as the first ' +
					'argument for mustache#render(template, view, partials)');
			}

			return defaultWriter.render(template, view, partials);
		};

		// This is here for backwards compatibility with 0.4.x.,
		/*eslint-disable */ // eslint wants camel cased function name
		mustache.to_html = function to_html(template, view, partials, send) {
			/*eslint-enable*/

			var result = mustache.render(template, view, partials);

			if (isFunction(send)) {
				send(result);
			} else {
				return result;
			}
		};

		// Export the escaping function so that the user may override it.
		// See https://github.com/janl/mustache.js/issues/244
		mustache.escape = escapeHtml;

		// Export these mainly for testing, but also for advanced usage.
		mustache.Scanner = Scanner;
		mustache.Context = Context;
		mustache.Writer = Writer;

		return mustache;
	}));
});

function PageManager() {
	SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> function PageManager() {');
	this.curPage = null;
	this.pages = {};
}
var yesNoReplyButtonCheckFlag = false;
var prev_page_visited_temp = 0;
var isCheckTypeRecordFreez = 0;

var checkType = "";
PageManager.prototype = {
	addPage(pageName, page) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> addPage(pageName, page) {');
		this.pages[pageName] = page;
	},
	reloadPreviousPage(pageName) {
		// 2017/02/01 Added By Abhijeet - #58,#59(Merge answer recordings which are created on replay button) -start
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> reloadPage(pageName) ' + pageName);
		SPKManage.writeLog(`showPage: ${pageName}`);

		releaseNativeVideoBridge();

		hideReplayButton();

		jquery$1('audio,video').off('abort emptied').each(function (i, elem) {
			elem.pause();
			elem.removeAttribute('src');
			elem.load();
		});

		var newPage = this.pages[pageName];

		if (!newPage) {
			alert('page not found.[' + pageName + ']');
			return
		}

		if (this.curPage) {
			if (this.curPage['pageWillDisappear']) {
				this.curPage.pageWillDisappear(this);
			}
		}

		if (SRState.prev_page_name != pageName) {
			SRState.prev_page_visited = 1;
			SRState.reload_count = 0;
		}/*else{
			SRState.prev_page_visited=SRState.prev_page_visited+1;
		}*/
		if (yesNoReplyButtonCheckFlag) {
			SRState.prev_page_visited = prev_page_visited_temp + 2;
			yesNoReplyButtonCheckFlag = false;
		}
		SPKManage.writeLog('#####SRState.prev_page_visited:' + SRState.prev_page_visited);

		SRState.prev_page_name = SRState.curPageName;

		this.curPage = newPage;

		var html = this.curPage.pageWillAppear(this);
		if (html) {
			SRState.curPageName = pageName;
			setState(SRState);
			SPKManage.loadContent(html);
			return
		}
		// 2017/02/01 Added By Abhijeet - #58,#59(Merge answer recordings which are created on replay button) -end
	},

	showPage(pageName) {

		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> showPage(pageName) {');
		SPKManage.writeLog(`showPage: ${pageName}`);

		releaseNativeVideoBridge();

		hideReplayButton();

		jquery$1('audio,video').off('abort emptied').each(function (i, elem) {
			elem.pause();
			elem.removeAttribute('src');
			elem.load();
		});

		SPKManage.stopRecordSound(100);
		SPKManage.stopMicCheck();
		SPKManage.delTempFile();

		var newPage = this.pages[pageName];

		if (!newPage) {
			alert('page not found.[' + pageName + ']');
			return
		}

		if (this.curPage) {
			if (this.curPage['pageWillDisappear']) {
				this.curPage.pageWillDisappear(this);
			}
		}

		/*Added following code for replay button press count
		 * Abhijeet 2017/12/14
		 * Ticket No. CBT-57
		 */
		if (SRState.prev_page_name != pageName) {
			SRState.prev_page_visited = 1;
			SRState.reload_count = 0;
		}
		if (checkType === "record" && isCheckTypeRecordFreez != 0) {
			SRState.prev_page_visited = isCheckTypeRecordFreez;
			SPKManage.writeLog('#####SRState.prev_page_visit for record type' + SRState.prev_page_visited);
			isCheckTypeRecordFreez = 0;
		}
		/*else{
			SRState.prev_page_visited=SRState.prev_page_visited+1;
		}*/
		//END Ticket No. CBT-57

		/*Added following code to store Precious Page Name
		 * Abhijeet 2017/12/13
		 * Ticket No. CBT-55
		 */
		SRState.prev_page_name = SRState.curPageName;
		////END Ticket No. CBT-57		

		this.curPage = newPage;

		var html = this.curPage.pageWillAppear(this);
		if (html) {
			SRState.curPageName = pageName;
			setState(SRState);
			SPKManage.loadContent(html);
			return
		}

	},

	resumePage(pageName) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> resumePage(pageName) {');
		SPKManage.writeLog(`resumePage: ${pageName}`);
		this.curPage = this.pages[pageName];
		if (this.curPage && this.curPage['pageDidAppear']) {
			this.curPage.pageDidAppear(this);
		}
	},

	renderTemplate(tmplName) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> renderTemplate(tmplName) {');
		var param = [], len = arguments.length - 1;
		while (len-- > 0) param[len] = arguments[len + 1];

		SPKManage.writeLog(`renderTemplate: ${tmplName}`);
		var tmpl = SPKManage.getHtmlTemplate(`tmpl/${tmplName}`);
		var context = Object.assign.apply(Object, [{
			base: SPKManage.getHtmlBaseURI(),
		}].concat(param));

		return mustache.render(tmpl, context)
	},
};

function BookletManager() {
	SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> function BookletManager() {');
	this.testset = null;
	this.scripts = null;
	this.breadcrumbs = null;
	this.booklet_name = null;
	this.booklet_id = null;
}

BookletManager.prototype = {
	load(booklet_name) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> load(booklet_name) {');
		var json_testset = SPKManage.readTestSetJson(booklet_name);
		if (!json_testset) {
			alert(`Booklet json not found: ${booklet_name}`);
			return false
		}

		var json_scripts = SPKManage.readTestSetScriptJson(booklet_name);
		if (!json_scripts) {
			alert(`Booklet scripts json not found: ${booklet_name}`);
			return false
		}

		var json_breadcrumbs = SPKManage.readTestSetBreadcrumbsJson(booklet_name);
		if (!json_breadcrumbs) {
			alert(`Booklet breadcrumbs json not found: ${booklet_name}`);
			return false
		}


		var testset = JSON.parse(json_testset);
		if (!testset) {
			alert(`Booklet json parse error: ${booklet_name}`);
			return false
		}
		if (!testset[booklet_name]) {
			alert(`Booklet json illegal format error: ${booklet_name}`);
			return false
		}

		var booklet_id = testset[booklet_name].booklet_id;

		var scripts = JSON.parse(json_scripts);
		if (!scripts) {
			alert(`Booklet scripts json parse error: ${booklet_name}`);
			return false
		}

		if (!scripts[booklet_id]) {
			alert(`Booklet scripts json illegal format error: ${booklet_name} ${booklet_id}`);
			return false
		}

		var breadcrumbs = JSON.parse(json_breadcrumbs);
		if (!breadcrumbs) {
			alert(`Booklet breadcrumbs json parse error: ${booklet_name}`);
			return false
		}

		this.booklet_name = booklet_name;
		this.booklet_id = booklet_id;
		this.testset = testset[booklet_name];
		this.scripts = scripts[booklet_id];
		this.breadcrumbs = breadcrumbs;

		return true
	},

	// 問題データ情報の取得
	getInterviews() {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> getInterviews() {');
		var this$1 = this;

		var interviews = [];

		if (this.scripts) {
			for (var i = 0; i < this.scripts.length; ++i) {
				var arr = this$1.scripts[i];
				var keys = Object.keys(arr);
				for (var j = 0; j < keys.length; ++j) {
					interviews.push(this$1.getInterviewInfo(keys[j]));
				}
			}
		}

		return interviews
	},

	// インタビュー情報の取得
	getInterviewInfo(interview_id) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> getInterviewInfo(interview_id) {');
		var emptyInfo = {
		};

		if (!this.scripts) {
			return emptyInfo
		}

		var _scripts = this.scripts;
		var scriptInfo = {};

		for (var i = 0; i < _scripts.length; ++i) {
			var arr = _scripts[i];
			var keys = Object.keys(arr);
			for (var j = 0; j < keys.length; ++j) {
				var key = keys[j];
				scriptInfo[key] = arr[key];
			}
		}

		if (!scriptInfo[interview_id]) {
			return emptyInfo
		}

		var interview = Object.assign({
			record_file_name: '',
			part_no: 0,
			is_recording: 0,
			interview_script_element: [],

			interview_id: interview_id,
		}, scriptInfo[interview_id]);

		interview.part_no = parseInt(interview.part_no, 10) || 0;
		interview.is_recording = parseInt(interview.is_recording, 10) || 0;
		// interview.interview_script_element = this.getScriptElements(interview_id)

		return interview
	},

	// interview_script_element の取得
	getScriptElements(interview_id) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> getScriptElements(interview_id) {');
		var interviewInfo = this.getInterviewInfo(interview_id);

		var scriptElements = [];
		for (var i = 0; i < interviewInfo.interview_script_element.length; ++i) {
			var element = Object.assign({
				element_page_id: '',
				index: 0,
				type: '',
				duration: 0,
				audio_filename: '',
				images: '',
				alarm_sound: 0,
				is_countdown: 0,
			}, interviewInfo.interview_script_element[i]);

			element.index = parseInt(element.index, 10) || 0;
			element.duration = parseInt(element.duration, 10) || 0;
			element.alarm_sound = parseInt(element.alarm_sound, 10) || 0;
			element.is_countdown = parseInt(element.is_countdown, 10) || 0;

			scriptElements.push(element);
		}

		return scriptElements
	},

	// パンくず情報の取得
	getBreadcrumbInfo(interview_id, element_id) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> getBreadcrumbInfo(interview_id, element_id) {');
		var emptyInfo = {
			//section_max: 0,
			//section_now: 0,
			task_name: '',
			task_max: 0,
			task_now: 0,
			state_max: 0,
			state_now: 0,
			state_str: [],
		};

		if (!this.breadcrumbs) {
			return emptyInfo
		}

		var elements = this.breadcrumbs[interview_id];

		if (!elements) {
			return emptyInfo
		}

		for (var i = 0; i < elements.length; ++i) {
			var info = elements[i][element_id];
			if (info) {
				//info.section_max = parseInt(info.section_max, 10) || 0;
				//info.section_now = parseInt(info.section_now, 10) || 0;
				info.task_max = parseInt(info.task_max, 10) || 0;
				info.task_now = parseInt(info.task_now, 10) || 0;
				info.state_max = parseInt(info.state_max, 10) || 0;
				info.state_now = parseInt(info.state_now, 10) || 0;

				return info
			}
		}

		return emptyInfo
	},


};

// error handler
window.onerror = function (msg, file, line, column, err) {
	alert(`${msg} ${file}: ${line}: ${column}: ${err}`);
	SPKManage.writeLog(`${msg} ${file}: ${line}: ${column}: ${err}`);
	TerminateTimers();
};

//
window.spk_error = wrap_log(function (msg) {
	throw new Error(msg)
});

// video 状態ログ用ヘルパー（動画黒画面調査用 instrumentation）
function mediaState(elem, label, extra) {
	var rect = elem.getBoundingClientRect ? elem.getBoundingClientRect() : {};
	var computed = window.getComputedStyle ? window.getComputedStyle(elem) : null;
	var err = elem.error;
	return Object.assign({
		label: label,
		t: (window.performance && performance.now) ? performance.now() : Date.now(),
		tag: elem.tagName,
		isConnected: !!elem.isConnected,
		documentContains: document.contains ? document.contains(elem) : null,
		src: elem.getAttribute('src') || '',
		currentSrc: elem.currentSrc || '',
		readyState: elem.readyState,
		networkState: elem.networkState,
		currentTime: elem.currentTime,
		duration: elem.duration,
		paused: elem.paused,
		ended: elem.ended,
		videoWidth: elem.videoWidth || 0,
		videoHeight: elem.videoHeight || 0,
		display: elem.style.display || '',
		visibility: elem.style.visibility || '',
		computedDisplay: computed ? computed.display : '',
		computedVisibility: computed ? computed.visibility : '',
		computedOpacity: computed ? computed.opacity : '',
		rect: {
			x: rect.x || 0,
			y: rect.y || 0,
			width: rect.width || 0,
			height: rect.height || 0
		},
		error: err ? { code: err.code, message: err.message } : null
	}, extra || {});
}

function logMedia(elem, label, extra) {
	try {
		SPKManage.writeLog('[video-state] ' + JSON.stringify(mediaState(elem, label, extra)));
	} catch (e) {
		console.warn('video-state log failed', e);
	}
}

// MeargeFileThread（MP3 マージ・暗号化）完了後に callback を実行する（Gate 2）
function waitForMp3EncryptionReady(callback, phase) {
	var pollMs = 200;
	var maxWaitMs = 60000;
	var elapsed = 0;
	var startedAt = Date.now();

	function poll() {
		if (SPKManage.isMeargingCompleted()) {
			callback(Date.now() - startedAt, phase || 'default');
			return;
		}
		elapsed += pollMs;
		if (elapsed >= maxWaitMs) {
			SPKManage.writeLog('[video-state] waitForMp3EncryptionReady: timeout after ' + maxWaitMs + 'ms, proceeding phase=' + (phase || 'default'));
			callback(Date.now() - startedAt, phase || 'default');
			return;
		}
		setTimeout(poll, pollMs);
	}
	poll();
}

// error 4 リトライ前の固定待ち（reloadPage 直後の連続 load を間引く）
var VIDEO_ERROR4_RETRY_DELAY_MS = 80;
// Gate 5V: pixel sampling + bounded reinit
var VIDEO_PIXEL_SAMPLE_RAF = 3;
var VIDEO_REINIT_MAX_ATTEMPTS = 2;
var NATIVE_VIDEO_GATE5V_RETRY_MAX = 4;
var NATIVE_VIDEO_GATE5V_RETRY_DELAY_MS = 200;
var NATIVE_VIDEO_REINIT_SETTLE_MS = 200;
var video_flow_id_seq = 0;

// MediaPlayer + MediaView（Java bridge）— WebView 内 HTML5 video の代替
function isNativeVideoBridgeEnabled() {
	try {
		return !!SPKManage.isNativeVideoEnabled();
	} catch (e) {
		return false;
	}
}

function installNativeVideoGlobalBridge() {
	window.__nativeVideoOnEvent = function (event, data) {
		if (window.__nativeVideoHandler) {
			window.__nativeVideoHandler(event, data || {});
		}
	};
}

function releaseNativeVideoBridge() {
	try {
		window.__nativeVideoHandler = null;
		resetNativeVideoModalSuppress();
		SPKManage.releaseNativeVideo();
	} catch (e) {}
}

function releaseNativeVideoPlayerOnly() {
	try {
		SPKManage.releaseNativeVideo();
	} catch (e) {}
}

var spkModalNativeVideoSuppressDepth = 0;

function resetNativeVideoModalSuppress() {
	spkModalNativeVideoSuppressDepth = 0;
}

function pauseNativeVideoPlayback() {
	try {
		if (isNativeVideoBridgeEnabled()) {
			SPKManage.pauseNativeVideo();
		}
	} catch (e) {}
}

function suppressNativeVideoForModal() {
	if (spkModalNativeVideoSuppressDepth === 0) {
		pauseNativeVideoPlayback();
		try {
			if (isNativeVideoBridgeEnabled()) {
				SPKManage.setNativeVideoVisible(false);
			}
		} catch (e) {}
	}
	spkModalNativeVideoSuppressDepth += 1;
}

function restoreNativeVideoAfterModal(skipRestore) {
	if (spkModalNativeVideoSuppressDepth <= 0) {
		return;
	}
	spkModalNativeVideoSuppressDepth -= 1;
	if (skipRestore || spkModalNativeVideoSuppressDepth > 0) {
		return;
	}
	try {
		if (isNativeVideoBridgeEnabled()) {
			SPKManage.setNativeVideoVisible(true);
		}
	} catch (e) {}
}

function showSpkBodyModal(modalClass) {
	suppressNativeVideoForModal();
	jquery$1('body').addClass('showalert ' + modalClass);
}

function hideSpkBodyModal(modalClass, skipRestore) {
	jquery$1('body').removeClass('showalert ' + modalClass);
	restoreNativeVideoAfterModal(!!skipRestore);
}

function showSpkElementModal(elementId) {
	suppressNativeVideoForModal();
	var el = document.getElementById(elementId);
	if (!el) {
		return;
	}
	el.style.visibility = 'visible';
	el.style.opacity = 1;
}

function hideSpkElementModal(elementId, skipRestore) {
	var el = document.getElementById(elementId);
	if (el) {
		el.style.visibility = 'hidden';
		el.style.opacity = 0;
	}
	restoreNativeVideoAfterModal(!!skipRestore);
}

function nativeVideoAbsolutePath(src) {
	var name = String(src || '').replace(/\\/g, '/');
	var slash = name.lastIndexOf('/');
	if (slash >= 0) {
		name = name.substring(slash + 1);
	}
	return SPKManage.getDecryptedFileDir() + name;
}

function shouldUseNativeVideoSrc(src) {
	if (!isNativeVideoBridgeEnabled()) {
		return false;
	}
	var s = String(src || '').replace(/\\/g, '/');
	return s.indexOf('/tmp/') >= 0 || s.indexOf('../../../tmp/') >= 0;
}

function applyNativeVideoPlaceholderSize(elem, videoWidth, videoHeight) {
	if (!elem || !elem._nativeVideoMode) {
		return;
	}
	var vw = videoWidth || elem._nativeVideoWidth || 480;
	var vh = videoHeight || elem._nativeVideoHeight || 270;
	if (vw <= 0 || vh <= 0) {
		return;
	}
	var containerWidth = elem.offsetWidth;
	if (containerWidth <= 0 && elem.parentElement) {
		containerWidth = elem.parentElement.clientWidth;
	}
	if (containerWidth <= 0) {
		var rect = elem.getBoundingClientRect ? elem.getBoundingClientRect() : {};
		containerWidth = rect.width || 0;
	}
	if (containerWidth <= 0) {
		return;
	}
	var height = Math.round(containerWidth * vh / vw);
	elem.style.width = '100%';
	elem.style.height = height + 'px';
	elem.style.minHeight = height + 'px';
	elem.style.display = 'block';
}

function prepareHtmlVideoForNative(elem) {
	elem._nativeVideoMode = true;
	elem._nativeVideoWidth = elem._nativeVideoWidth || 480;
	elem._nativeVideoHeight = elem._nativeVideoHeight || 270;
	elem.muted = true;
	elem.setAttribute('muted', 'muted');
	elem.style.visibility = 'hidden';
	elem.style.pointerEvents = 'none';
	elem.removeAttribute('src');
	try {
		elem.load();
	} catch (e) {}
	applyNativeVideoPlaceholderSize(elem);
	requestAnimationFrame(function () {
		applyNativeVideoPlaceholderSize(elem);
	});
}

function syncNativeVideoBounds(elem) {
	if (elem && elem._nativeVideoMode) {
		applyNativeVideoPlaceholderSize(elem);
	}
	var rect = elem.getBoundingClientRect ? elem.getBoundingClientRect() : {};
	SPKManage.setNativeVideoBounds(rect.left || 0, rect.top || 0, rect.width || 0, rect.height || 0);
}

function registerNativeVideoHandler(handler) {
	window.__nativeVideoHandler = handler;
}

function set_native_video_event_handler($elem, filename) {
	var videoEvents = [
		'abort', 'emptied', 'stalled', 'canplay', 'canplaythrough',
		'durationchange', 'loadeddata', 'loadedmetadata', 'loadstart',
		'pause', 'play', 'playing', 'progress', 'ratechange', 'seeked',
		'suspend', 'timeupdate', 'volumechange', 'waiting', 'ended'
	];
	videoEvents.forEach(function (eventName) {
		$elem.on(eventName + '.native-bridge', wrap_log(function (event) {
			logMedia(event.target, 'native-bridge:html-' + eventName, { filename: filename });
		}));
	});
}

function set_native_video_retry_handler($elem, filename, src) {
	var count_video_retry = 0;
	$elem.each(function (i, elem) {
		elem._nativeRetrySrc = src;
		elem._nativeRetryFilename = filename;
		elem._nativeRetryCount = 0;
		elem._nativeGate5vRetryCount = 0;
	});
}

function retryNativeVideoLoad(elem) {
	var count = (elem._nativeRetryCount || 0) + 1;
	elem._nativeRetryCount = count;
	if (count >= 150) {
		throw new Error('動画の再生に失敗しました [' + (elem._nativeRetryFilename || '') + ']');
	}
	SPKManage.writeLog('retry native video: retry=' + count + ': ' + (elem._nativeRetryFilename || ''));
	setTimeout(function () {
		syncNativeVideoBounds(elem);
		SPKManage.prepareNativeVideo(nativeVideoAbsolutePath(elem._nativeRetrySrc), elem._nativeRetryFilename);
	}, VIDEO_ERROR4_RETRY_DELAY_MS > 0 ? VIDEO_ERROR4_RETRY_DELAY_MS : 0);
}

function prepareNativeVideoWithReinit(elem, src, fname, useForceReinit, callback) {
	if (useForceReinit) {
		try {
			SPKManage.forceReinitNativeVideo();
		} catch (e) {}
	}
	setTimeout(function () {
		syncNativeVideoBounds(elem);
		SPKManage.prepareNativeVideo(nativeVideoAbsolutePath(src), fname);
		if (typeof callback === 'function') {
			callback();
		}
	}, useForceReinit ? NATIVE_VIDEO_REINIT_SETTLE_MS : 0);
}

function retryNativeVideoForGate5v(elem, sr, makeFlowCtx) {
	pauseNativeVideoPlayback();
	var count = (elem._nativeGate5vRetryCount || 0) + 1;
	elem._nativeGate5vRetryCount = count;
	if (count > NATIVE_VIDEO_GATE5V_RETRY_MAX) {
		throw new Error('動画の再生に失敗しました [' + (elem._nativeRetryFilename || sr.fname || '') + ']');
	}
	SPKManage.writeLog('retry native video gate5v: retry=' + count + ': ' + (elem._nativeRetryFilename || sr.fname || ''));
	var src = elem._nativeRetrySrc || sr.src;
	var fname = elem._nativeRetryFilename || sr.fname;
	sr._pendingNativeDirectPlay = true;
	setTimeout(function () {
		releaseNativeVideoPlayerOnly();
		prepareNativeVideoWithReinit(elem, src, fname, true);
	}, NATIVE_VIDEO_GATE5V_RETRY_DELAY_MS);
}

function bindNativeVideoPlayback($elem, filename, src, opts) {
	if (!shouldUseNativeVideoSrc(src)) {
		set_video_event_handler($elem, filename);
		set_video_retry_handler($elem, filename, src);
		return false;
	}
	opts = opts || {};
	$elem.each(function (i, elem) {
		prepareHtmlVideoForNative(elem);
		elem._nativeRetrySrc = src;
		elem._nativeRetryFilename = filename;
		set_native_video_event_handler($elem, filename);
	});
	if (opts.autoPrepare) {
		$elem.each(function (i, elem) {
			syncNativeVideoBounds(elem);
			SPKManage.prepareNativeVideo(nativeVideoAbsolutePath(src), filename);
		});
	}
	return true;
}

function startNativeVideoLoadForSRVideo(sr, elem, makeFlowCtx) {
	var ctx = makeFlowCtx('preSrc');
	ctx.isTokenValid = function () {
		return true;
	};
	prepareHtmlVideoForNative(elem);
	prepareVideoPlayback(elem, 'preSrc', ctx, function (timing) {
		logMedia(elem, 'SRVideo:native:prepare', Object.assign({}, videoFlowExtra(ctx), timing, {
			fname: sr.fname,
			nativePath: nativeVideoAbsolutePath(sr.src)
		}));
		syncNativeVideoBounds(elem);
		SPKManage.prepareNativeVideo(nativeVideoAbsolutePath(sr.src), sr.fname);
	});
}

function runNativeDirectPlayForSRVideo(sr, elem, makeFlowCtx) {
	var ctx = makeFlowCtx('prePlay');
	prepareVideoPlayback(elem, 'prePlay', ctx, function (timing) {
		logMedia(elem, 'SRVideo:native:direct-play', Object.assign({}, videoFlowExtra(ctx), timing, { fname: sr.fname }));
		syncNativeVideoBounds(elem);
		applyNativeVideoVolumeFromState();
		SPKManage.playNativeVideo();
	});
}

function handleNativeVideoEventForSRVideo(sr, elem, event, data, makeFlowCtx) {
	if (data && data.fname && data.fname !== sr.fname) {
		return;
	}
	var ctx = makeFlowCtx('native-' + event);
	logMedia(elem, 'native-video:' + event, Object.assign({}, videoFlowExtra(ctx), { data: data, fname: sr.fname }));

	if (event === 'ready') {
		elem._nativeVideoWidth = data.videoWidth || 0;
		elem._nativeVideoHeight = data.videoHeight || 0;
		applyNativeVideoPlaceholderSize(elem, data.videoWidth, data.videoHeight);
		syncNativeVideoBounds(elem);
		applyNativeVideoVolumeFromState();
		if (typeof whenVideoLoaded === 'function') {
			whenVideoLoaded();
		}
		if (sr._pendingNativeDirectPlay) {
			sr._pendingNativeDirectPlay = false;
		}
		if (sr._nativeAwaitingReinitPlay) {
			sr._nativeAwaitingReinitPlay = false;
		}
		runNativeDirectPlayForSRVideo(sr, elem, makeFlowCtx);
		return;
	}
	if (event === 'playing') {
		syncNativeVideoBounds(elem);
		applyNativeVideoVolumeFromState();
		if (typeof whenVideoPlaying === 'function') {
			whenVideoPlaying();
		}
		sr.opt.play();
		return;
	}
	if (event === 'gate5v-black-failed') {
		retryNativeVideoForGate5v(elem, sr, makeFlowCtx);
		return;
	}
	if (event === 'ended') {
		jquery$1(elem).trigger('ended');
		return;
	}
	if (event === 'error') {
		if (data && data.reason === 'overlay-not-ready') {
			syncNativeVideoBounds(elem);
		}
		retryNativeVideoLoad(elem);
	}
}

function nextVideoFlowId() {
	video_flow_id_seq += 1;
	return 'vf-' + video_flow_id_seq;
}

function videoFlowExtra(ctx) {
	if (!ctx) {
		return {};
	}
	return {
		generation: ctx.generation,
		reinitCount: ctx.reinitCount,
		videoFlowId: ctx.videoFlowId,
		phase: ctx.phase
	};
}

function waitAnimationFrames(count, callback) {
	var remaining = count;
	function step() {
		remaining -= 1;
		if (remaining <= 0) {
			callback();
			return;
		}
		requestAnimationFrame(step);
	}
	requestAnimationFrame(step);
}

// Gate 5V: opt.play() 前の pixel sample
function waitForVisiblePixels(elem, ctx, callback) {
	waitAnimationFrames(VIDEO_PIXEL_SAMPLE_RAF, function () {
		if (ctx && ctx.isTokenValid && !ctx.isTokenValid()) {
			callback({ pass: false, reason: 'token-invalidated' });
			return;
		}
		if (elem.readyState < HTMLMediaElement.HAVE_CURRENT_DATA) {
			logMedia(elem, 'pixel-sample:fail', Object.assign({}, videoFlowExtra(ctx), {
				reason: 'readyState',
				readyState: elem.readyState
			}));
			callback({ pass: false, reason: 'readyState', readyState: elem.readyState });
			return;
		}
		if ((elem.videoWidth || 0) <= 0 || (elem.videoHeight || 0) <= 0) {
			logMedia(elem, 'pixel-sample:fail', Object.assign({}, videoFlowExtra(ctx), {
				reason: 'videoSize',
				videoWidth: elem.videoWidth || 0,
				videoHeight: elem.videoHeight || 0
			}));
			callback({ pass: false, reason: 'videoSize' });
			return;
		}
		var sample = null;
		try {
			sample = sampleVideoPixels(elem);
		} catch (e) {
			if (isPixelSampleSecurityError(e)) {
				logMedia(elem, 'pixel-sample:skipped', Object.assign({}, videoFlowExtra(ctx), {
					reason: 'security-error',
					error: String(e)
				}));
				callback({ pass: true, skipped: true, reason: 'security-error', error: String(e) });
				return;
			}
			logMedia(elem, 'pixel-sample:fail', Object.assign({}, videoFlowExtra(ctx), {
				reason: 'sample-error',
				error: String(e)
			}));
			callback({ pass: false, reason: 'sample-error', error: String(e) });
			return;
		}
		if (sample && sample.unavailable) {
			logMedia(elem, 'pixel-sample:skipped', Object.assign({}, videoFlowExtra(ctx), {
				reason: sample.reason || 'unavailable',
				error: sample.error || null
			}));
			callback({
				pass: true,
				skipped: true,
				reason: sample.reason || 'unavailable',
				sample: sample
			});
			return;
		}
		logMedia(elem, sample.blackCandidate ? 'pixel-sample:fail' : 'pixel-sample:pass', Object.assign({}, videoFlowExtra(ctx), {
			sample: sample,
			blackCandidate: sample.blackCandidate
		}));
		callback({
			pass: !sample.blackCandidate,
			sample: sample,
			blackCandidate: sample.blackCandidate
		});
	});
}

// prepareVideoPlayback: Gate 集約オーケストレータ
function prepareVideoPlayback(elem, phase, ctx, callback) {
	ctx.phase = phase;

	if (phase === 'preSrc') {
		var pipelineStartedAt = Date.now();
		waitForMp3EncryptionReady(function (mergeWaitMs) {
			callback(Object.assign({
				mergeWaitMs: mergeWaitMs,
				pipelineWaitMs: Date.now() - pipelineStartedAt
			}, videoFlowExtra(ctx)));
		});
		return;
	}

	if (phase === 'prePlay') {
		// 開始時黒画面は Native Gate 5V / HTML5 pixel-sample で検出する。
		callback(Object.assign({
			prePlayGateSkipped: true
		}, videoFlowExtra(ctx)));
		return;
	}

	if (phase === 'preOptPlay') {
		waitForVisiblePixels(elem, ctx, function (result) {
			callback(Object.assign({}, result, videoFlowExtra(ctx)));
		});
		return;
	}

	callback(videoFlowExtra(ctx));
}

// Gate 2（後方互換ラッパ）
function whenVideoPipelineReady(elem, callback, ctx) {
	var flowCtx = ctx || {
		generation: 0,
		reinitCount: 0,
		videoFlowId: nextVideoFlowId(),
		fname: '',
		isTokenValid: null
	};
	prepareVideoPlayback(elem, 'preSrc', flowCtx, callback);
}

function isPixelSampleSecurityError(err) {
	if (!err) {
		return false;
	}
	var name = err.name || '';
	var message = String(err.message || err);
	return name === 'SecurityError'
		|| message.indexOf('SecurityError') >= 0
		|| message.indexOf('operation is insecure') >= 0;
}

function sampleVideoPixels(elem) {
	var canvas = document.createElement('canvas');
	canvas.width = 32;
	canvas.height = 18;
	var ctx = canvas.getContext('2d', { willReadFrequently: true });
	try {
		ctx.drawImage(elem, 0, 0, canvas.width, canvas.height);
	} catch (e) {
		if (isPixelSampleSecurityError(e)) {
			return {
				unavailable: true,
				reason: 'security-error',
				error: String(e)
			};
		}
		throw e;
	}
	var data;
	try {
		data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
	} catch (e) {
		if (isPixelSampleSecurityError(e)) {
			return {
				unavailable: true,
				reason: 'security-error',
				error: String(e)
			};
		}
		throw e;
	}
	var min = 255;
	var max = 0;
	var sum = 0;
	var count = 0;
	for (var i = 0; i < data.length; i += 4) {
		var luma = Math.round(0.2126 * data[i] + 0.7152 * data[i + 1] + 0.0722 * data[i + 2]);
		min = Math.min(min, luma);
		max = Math.max(max, luma);
		sum += luma;
		count += 1;
	}
	var avg = count ? sum / count : 0;
	return {
		min: min,
		max: max,
		avg: avg,
		blackCandidate: max < 8 && avg < 4
	};
}

var srvideo_play_call_count = 0;

// MediaError 文字列の取得
function get_media_error_string(err) {
	var msg = err.message;
	var s = "";

	switch (err.code) {
		case MediaError.MEDIA_ERR_ABORTED:
			s += "The user canceled the audio/video.";
			break
		case MediaError.MEDIA_ERR_NETWORK:
			s += "A network error occurred while fetching the audio/video.";
			break
		case MediaError.MEDIA_ERR_DECODE:
			s += "An error occurred while decoding the audio/video.";
			break
		case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
			s += "The audio/video is missing or is in a format not supported by your browser.";
			break
		default:
			s += "An unknown error occurred.";
			break
	}
	if (msg && msg.length) {
		s += " " + msg;
	}

	return s
}

// video タグのエラーハンドラ設定
function set_video_error_handler($elem, filename) {
	SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> function set_video_error_handler($elem, filename) {');
	$elem
		.on('error', wrap_log(function (event) {
			var target = event.target;
			var err = target.error;
			var msg = get_media_error_string(err);

			logMedia(target, 'video-error:fatal', {
				filename: filename,
				errorCode: err ? err.code : null,
				errorMessage: err ? err.message : null,
				errorString: msg
			});
			jquery$1('.alertbox .txt').html('エラーが発生しました。試験監督者を呼んでください。');
			throw new Error(`動画の再生に失敗しました [${filename}]`)
		}));
}

// video タグのリトライハンドラ設定
function set_video_retry_handler($elem, filename, src) {
	SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> function set_video_retry_handler($elem, filename, src) {');
	var count_video_retry = 0;

	function assignRetrySrc(target, retryCount, opts) {
		if (opts && opts.error4Recover) {
			target._videoError4RecoverPending = true;
		} else {
			target._videoError4RecoverPending = false;
		}
		target.src = src;
		logMedia(target, 'video-retry:after-assign-src', {
			filename: filename,
			retryCount: retryCount,
			retrySrc: src,
			error4Recover: !!(opts && opts.error4Recover)
		});
		target.load();
	}

	$elem
		.on('error', wrap_log(function (event) {
			var target = event.target;
			var err = target.error;
			var msg = get_media_error_string(err);

			logMedia(target, 'video-error:retry', {
				filename: filename,
				retryCount: count_video_retry,
				errorCode: err ? err.code : null,
				errorMessage: err ? err.message : null,
				errorString: msg,
				retrySrc: src
			});

			count_video_retry += 1;
			SPKManage.writeLog(`retry load video: retry=${count_video_retry}: ${filename}`);

			if (count_video_retry >= 150) {
				jquery$1(target).off('error');
				set_video_error_handler(jquery$1(target), filename);
			}

			target.removeAttribute('src');

			var isError4 = err && err.code === MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED;
			if (isError4 && VIDEO_ERROR4_RETRY_DELAY_MS > 0) {
				setTimeout(function () {
					assignRetrySrc(target, count_video_retry, { error4Recover: true });
				}, VIDEO_ERROR4_RETRY_DELAY_MS);
			} else {
				assignRetrySrc(target, count_video_retry, isError4 ? { error4Recover: true } : null);
			}
		}));
}

// video タグのイベントハンドラ設定
function set_video_event_handler($elem, filename) {
	SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> function set_video_event_handler($elem, filename) {');
	var videoEvents = [
		'abort', 'emptied', 'stalled', 'canplay', 'canplaythrough',
		'durationchange', 'loadeddata', 'loadedmetadata', 'loadstart',
		'pause', 'play', 'playing', 'progress', 'ratechange', 'seeked',
		'suspend', 'timeupdate', 'volumechange', 'waiting', 'ended'
	];
	videoEvents.forEach(function (eventName) {
		$elem.on(eventName, wrap_log(function (event) {
			logMedia(event.target, 'video-event:' + eventName, { filename: filename });
			if (eventName === 'abort') {
				if (event.target._videoReinitInProgress || event.target._nativeVideoMode) {
					logMedia(event.target, 'video-event:abort:suppressed', {
						filename: filename,
						reason: event.target._nativeVideoMode ? 'native-video' : 'reinit-in-progress'
					});
					return;
				}
				throw new Error(`動画の再生に失敗しました [${filename}]`)
			}
		}));
	});
}

// ボリュームの設定
function setVolume(v) {
	SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> function setVolume(v) {');
	if (isNativeVideoBridgeEnabled()) {
		try {
			SPKManage.setNativeVideoVolume(v);
		} catch (e) {}
	}
	jquery$1('video,audio').each(function (i, elem) {
		elem.volume = 1;
		elem.volume = 0;
		elem.volume = v / 100;
	});
}

// HTML5 時代の loadeddata 相当: MediaPlayer 再生成前後に SRState.volume を掛け直す
function applyNativeVideoVolumeFromState() {
	if (!isNativeVideoBridgeEnabled()) {
		return;
	}
	try {
		SPKManage.setNativeVideoVolume(SRState.volume);
	} catch (e) {}
}

// ボリュームスライダーの初期化
function setupVolumeSlider() {
	SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> function setupVolumeSlider() {');
	SPKManage.writeLog('setup volume slider');

	var $volume = jquery$1('#volume');
	if ($volume.length > 0) {
		$volume.slider({
			tooltip: 'hide',
			value: SRState.volume,
		});

		$volume.on('change', wrap_log(function (event) {
			SRState.volume = event.value.newValue;
			setState(SRState);
			setVolume(SRState.volume);
		}));

		setVolume(SRState.volume);
	}
}

// 録音音声レベル表示処理
function setupRecordLevelMeter(opt) {
	SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> function setupRecordLevelMeter(opt) {');
	SPKManage.writeLog('setup record level meter');

	opt = Object.assign({
		is_recording() { return false },
		get_target() { return null },
		get_level() { return 0 },
	}, opt);

	var tick = function () {
		var lv = 0;
		var target = opt.get_target();
		if (opt.is_recording()) {
			lv = opt.get_level();
		}

		if (target) {
			target.setAttribute('data-volume', lv);
		}

		setTimeout(wrap_log(tick), 100);
	};
	tick();
}

function checkMP3FileSize(mp3Path, min_size) {
	SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> function checkMP3FileSize(mp3Path, min_size) {');
	if (min_size === void 0) min_size = 1024;

	var len = SPKManage.getFileSize(mp3Path);
	SPKManage.writeLog(`checkMP3FileSize: ${mp3Path} ${len} bytes`);
	if (len <= min_size) {
		throw new Error('音声解答の保存ができませんでした。RELOADボタンを押して、もう一度解答してください。')
	}
}


// ログイン画面
var PageLogin = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageLogin pageWillAppear(mgr) {');
		SPKManage.writeLog('page will appear: Login');

		return mgr.renderTemplate('login.html', {
		})
	},

	_login(userId, password) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageLogin _login(userId, password) {');
		SRState.curTestSetId = null;
		SRState.login_id = null;

		//throw new Error('THIS IS TESTING FOR LOGIN');
		var infoArray = (SPKManage.chkLogin(userId, password)).split("-");
		var loginCode = infoArray[0];

		var applyId = "";
		var year = "";
		var kaiji = "";

		if (infoArray.length > 1) {
			applyId = infoArray[1];
		}
		if (infoArray.length > 2) {
			year = infoArray[2];
		}
		if (infoArray.length > 2) {
			kaiji = infoArray[3];
		}

		SPKManage.writeLog('loginCode:' + loginCode + ';applyId:' + applyId + ';ExamYear:' + year + ';ExamKaiji:' + kaiji);

		if (loginCode !== 'I0001') {
			jquery$1('#js-login-msg').text(SPKManage.getLoginErrMess(loginCode));
			return false;
		}

		var curTestSetId = SPKManage.getCurTestSetId();

		SRState.curTestSetId = curTestSetId;
		SRState.login_id = userId;
		SRState.apply_id = applyId;
		SRState.year = year;
		SRState.kaiji = kaiji;

		return true
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageLogin1 pageDidAppear(mgr) {');
		var this$1 = this;

		SPKManage.writeLog('page did appear: Login');

		// マイク存在チェック
		if (!SPKManage.checkMicAvalable()) {
			throw new Error('マイクが接続されていません')
		}
		var check_info_level_error = false;
		// ログインボタン
		jquery$1('#js-sr-login').on('submit', wrap_log(function (event) {
			SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageLogin jquery$1("#js-sr-login").on("submit", wrap_log(function (event) {');
			var $form = jquery$1(event.target);
			var userId1 = $form.find('[name="userId1"]').val();
			var userId2 = $form.find('[name="userId2"]').val();
			var userId3 = $form.find('[name="userId3"]').val();
			var userId = `${userId1}${userId2}${userId3}`;
			var password = $form.find('[name="password"]').val();

			if (this$1._login(userId, password)) {
				var booklet_name = SRState.curTestSetId;

				var booklet_manager = new BookletManager();
				if (!booklet_manager.load(booklet_name)) {
					SRState.curTestSetId = null;
					SRState.login_id = null;
					jquery$1('#js-login-msg').text('試験データの読み込みに失敗しました');
					return false;
				}

				//added by Vijaya for EIK-18 start
				var resume_interview_id = null;
				var resumeid_and_statuscode = [];

				var status_code = null;
				var interview_element_part_no = null;

				resumeid_and_statuscode = SPKManage.getExamStatus(userId, booklet_name);

				if (resumeid_and_statuscode.length != 0) {
					if (resumeid_and_statuscode.length == 2) {
						interview_element_part_no = resumeid_and_statuscode[0];
						status_code = resumeid_and_statuscode[1];
						SPKManage.writeLog('=====22222=interview_element_part_no : ' + interview_element_part_no + "status_code : " + status_code);
					}
				}

				var resume_element_Id;

				var info_level_errorflag = false;

				if (interview_element_part_no != null) {

					var interview_element_part_errorflag = [];

					interview_element_part_errorflag = interview_element_part_no.split("-");

					if (interview_element_part_errorflag.length == 4) {
						info_level_errorflag = interview_element_part_errorflag[3];

						SPKManage.writeLog('=========>info_level_errorflag : ' + info_level_errorflag);

						if (info_level_errorflag == 'true') {
							check_info_level_error = info_level_errorflag;
							resume_interview_id = interview_element_part_errorflag[0];

							resume_element_Id = interview_element_part_errorflag[1];
							if (resume_element_Id == "NA") {
								resume_element_Id = "";
							}

						} else {

							if (status_code == 'S007' || status_code == 'S019' || status_code == 'S020' || status_code == 'S021' || status_code == 'S022' || status_code == 'S023') {
								SPKManage.setStatusCode(status_code);
								resume_interview_id = "last";
							} else {
								SPKManage.writeLog('====else=');
								if (interview_element_part_errorflag[0] == 'NA') {
									resume_interview_id = "first";
								} else {
									resume_interview_id = parseInt(interview_element_part_errorflag[0]) + 1;
								}
							}

						}

					} else {
						resume_interview_id = "first";
					}

				}

				if (interview_element_part_no == null) {
					resume_interview_id = "first";
				}

				//added by Vijaya for EIK-18 End

				//var resume_interview_id = SPKManage.getResumeInterviewId(userId, SRState.curTestSetId, SRState.apply_id, SRState.year, SRState.kaiji);
				SPKManage.writeLog('SRState.login_id:' + SRState.login_id + ':: SRState.curTestSetId:' + SRState.curTestSetId + ':: resume_interview_id' + resume_interview_id);
				SPKManage.loadOrCreateTestState(userId, SRState.curTestSetId, SRState.apply_id, SRState.year, SRState.kaiji);
				if (!resume_interview_id) {
					throw new Error('試験再開位置情報の読み込みに失敗しました')
				}


				if (resume_interview_id == "first") {
					mgr.showPage('Description1');
					SPKManage.normalizeTempFiles(SRState.login_id);
				} else if (resume_interview_id == "last") {
					mgr.showPage('EndExam');
				} else {
					var exam_pages = createExamPages(mgr);
					for (var i = 0; i < exam_pages.length; ++i) {
						var interview = exam_pages[i].interview;
						var element = exam_pages[i].element;

						var interview_id = interview.interview_id;
						var element_page_id = element.element_page_id;

						if (interview_id == resume_interview_id) {
							mgr.showPage(`Exam-${interview_id}-${element_page_id}`);
							if (i - 1 != 0) {
								var interviewPre = exam_pages[i - 1].interview;
								var interview_idPre = interviewPre.interview_id;
								SPKManage.setAnsweredInterviewId(SRState.login_id, SRState.curTestSetId, interview_idPre, SRState.apply_id, SRState.year, SRState.kaiji);
							}
							SPKManage.normalizeTempFiles(SRState.login_id);
							break;
						}
					}
				}
			}

			return false;
		}));

		var _userId = SPKManage.getInitialParam('examId');
		var _password = SPKManage.getInitialParam('password');
		if (_userId && _password && this._login(_userId, _password)) {
			var booklet_name = SRState.curTestSetId;

			var booklet_manager = new BookletManager();
			if (!booklet_manager.load(booklet_name)) {
				SRState.curTestSetId = null;
				SRState.login_id = null;
				throw new Error('試験データの読み込みに失敗しました')
			}
			//Commented by Mayuri #EIK-166 after successfuly(before content download) login send status to main server
			//var status_code = "S001"; //Added by Vijaya EIK-18 -> Logged In status 

			/*try{*/
			//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, "", status_code, false);//Commented by Mayuri #EIK-166
			/*}catch(Exception e){
				SPKManage.writeLog('exception in save Status after logged in');
			}*/
			//SPKManage.writeLog('After the method save Status after logged in');
			/*check_info_level_error=SPKManage.checkInfoLevelError(_userId);
			var pressAltCtrlC=SPKManage.getCtrlAltCFlag(_userId);
			SPKManage.writeLog('pressAltCtrlC flag::'+pressAltCtrlC);*/

			//added by Vijaya for EIK-18 start
			var resume_interview_id = null;
			var resumeid_and_statuscode = [];

			var status_code = null;
			var interview_element_part_no = null;

			resumeid_and_statuscode = SPKManage.getExamStatus(_userId, booklet_name); //to get resume position & status code from server

			//resumeid_and_statuscode[0] = "NA-false";
			//resumeid_and_statuscode[1] = "0001";


			if (resumeid_and_statuscode.length != 0) {
				if (resumeid_and_statuscode.length == 2) {
					interview_element_part_no = resumeid_and_statuscode[0];
					status_code = resumeid_and_statuscode[1];
					SPKManage.writeLog('======1111interview_element_part_no : ' + interview_element_part_no + "status_code : " + status_code);
				}
			}

			var resume_element_Id;

			var info_level_errorflag = false;

			if (interview_element_part_no != null) {

				var interview_element_part_errorflag = [];

				interview_element_part_errorflag = interview_element_part_no.split("-");

				if (interview_element_part_errorflag.length == 4) {
					info_level_errorflag = interview_element_part_errorflag[3];

					SPKManage.writeLog('=========>info_level_errorflag : ' + info_level_errorflag);

					if (info_level_errorflag == 'true') {
						check_info_level_error = info_level_errorflag;
						resume_interview_id = interview_element_part_errorflag[0];

						resume_element_Id = interview_element_part_errorflag[1];
						if (resume_element_Id == "NA") {
							resume_element_Id = "";
						}

					} else {

						if (status_code == 'S007' || status_code == 'S019' || status_code == 'S020' || status_code == 'S021' || status_code == 'S022' || status_code == 'S023') {
							SPKManage.setStatusCode(status_code);
							resume_interview_id = "last";
						} else {
							SPKManage.writeLog('====else=');
							if (interview_element_part_errorflag[0] == 'NA') {
								resume_interview_id = "first";
							} else {
								resume_interview_id = parseInt(interview_element_part_errorflag[0]) + 1;
							}
						}

					}

				} else {
					resume_interview_id = "first";
				}

			}

			if (interview_element_part_no == null) {
				resume_interview_id = "first";
			}

			//added by Vijaya for EIK-18 End

			//var resume_interview_id = SPKManage.getResumeInterviewId(_userId, SRState.curTestSetId, SRState.apply_id, SRState.year, SRState.kaiji);
			SPKManage.writeLog('SRState.login_id:' + SRState.login_id + ':: SRState.curTestSetId:' + SRState.curTestSetId + ':: resume_interview_id' + resume_interview_id);
			SPKManage.loadOrCreateTestState(_userId, SRState.curTestSetId, SRState.apply_id, SRState.year, SRState.kaiji);
			SPKManage.writeLog('=========>resume_interview_id' + resume_interview_id);
			if (!resume_interview_id) {
				throw new Error('試験再開位置情報の読み込みに失敗しました')
			}
			//Added by sharad for CBTIBA#135 start
			if (resume_interview_id == "first" && check_info_level_error == 'true') {
				var exam_pages = createExamPages(mgr);
				for (var i = 0; i < exam_pages.length; ++i) {
					var interview = exam_pages[i].interview;
					var element = exam_pages[i].element;
					var interview_id = interview.interview_id;
					var element_page_id = element.element_page_id;;
					//var resume_elementId=SPKManage.getResumeElementId(_userId);
					//SPKManage.writeLog('first:: resume_elementId:: '+resume_elementId);
					/*(resume_elementId!=null)
					{
						element_page_id=resume_elementId;
					}*/

					if (resume_element_Id != null) {
						element_page_id = resume_element_Id; //added by Vijaya
					}
					mgr.showPage(`Exam-${interview_id}-${element_page_id}`);
					//if(i-1 !=0 ){
					//var interviewPre = exam_pages[i-1].interview;
					//var interview_idPre = interviewPre.interview_id;
					//SPKManage.setAnsweredInterviewId(SRState.login_id, SRState.curTestSetId, interview_idPre,SRState.apply_id, SRState.year, SRState.kaiji);
					break
					//}
				}
			}
			else if (resume_interview_id == "last" && check_info_level_error == 'true') {
				var exam_pages = createExamPages(mgr);
				for (var i = 0; i < exam_pages.length; ++i) {
					var interview = exam_pages[i].interview;
					var element = exam_pages[i].element;
					var interview_id = interview.interview_id;
					var element_page_id = element.element_page_id;;
					//var resume_elementId=SPKManage.getResumeElementId(_userId);
					//SPKManage.writeLog('last:: resume_elementId:: '+resume_elementId);
					/*if(resume_elementId!=null)
					{
						element_page_id=resume_elementId;
					}*/
					if (resume_element_Id != null) {
						element_page_id = resume_element_Id;//added by Vijaya
					}
					mgr.showPage(`Exam-${interview_id}-${element_page_id}`);
					//if(i-1 !=0 ){
					//var interviewPre = exam_pages[i-1].interview;
					//var interview_idPre = interviewPre.interview_id;
					//SPKManage.setAnsweredInterviewId(SRState.login_id, SRState.curTestSetId, interview_idPre,SRState.apply_id, SRState.year, SRState.kaiji);
					break
					//}
				}
			}
			//Added by sharad for CBTIBA#135 End
			else if (resume_interview_id == "first") {
				mgr.showPage('Description1');
				SPKManage.normalizeTempFiles(SRState.login_id);
			} else if (resume_interview_id == "last") {
				mgr.showPage('EndExam');
			} else {
				var exam_pages = createExamPages(mgr);
				for (var i = 0; i < exam_pages.length; ++i) {
					var interview = exam_pages[i].interview;
					var element = exam_pages[i].element;

					var interview_id = interview.interview_id;

					var element_page_id = element.element_page_id;
					SPKManage.writeLog('interview_id>>>>>>>>>>>>>>' + interview_id);
					SPKManage.writeLog('resume_interview_id>>>>>>>>>>' + resume_interview_id);

					//Added by Vijaya EIK-18 for status management Start: when getStatus as last question
					if (i == exam_pages.length - 1) {
						//Added by Mayuri Show_Error_Popup_If_InterviewId_not_found_indownloaded_booklet#EIK-193 start
						var interviewID = parseInt(interview_id, 10) + 1;
						SPKManage.writeLog('Exam end interview id ' + interviewID);
						SPKManage.writeLog('Exam end resume interview_id>>>>>>>>>>' + resume_interview_id);
						//Added by Mayuri Show_Error_Popup_If_InterviewId_not_found_indownloaded_booklet#EIK-193 end
						if (resume_interview_id == interviewID) {
							SPKManage.writeLog('IN the inner if');
							resume_interview_id = "last";
							SPKManage.writeLog('IN the inner if resume_interview_id' + resume_interview_id);
							mgr.showPage('EndExam');
							break
						} else {//Added else condition by Mayuri Show_Error_Popup_If_InterviewId_not_found_indownloaded_booklet#EIK-193
							SPKManage.writeLog('Interview id not Exist>>>>>>>>>>>>>>' + interviewID);
							SPKManage.writeLog('Interview_id>>>>>>>>>>' + resume_interview_id);
							throw new Error('Interview id not Exist');
						}

					}
					//Added by Vijaya EIK-18 for status management Start: when getStatus as last question

					if (interview_id == resume_interview_id) {
						SPKManage.writeLog('check_info_level_error flag :  ' + check_info_level_error);
						if (check_info_level_error == 'true') {
							//var resume_elementId=SPKManage.getResumeElementId(_userId);
							//SPKManage.writeLog('resume_elementId ::'+resume_elementId);
							/*if(!pressAltCtrlC)
							{*/
							/*if(resume_elementId!==null)
							{	
								element_page_id=resume_elementId;
							}*/

							if (resume_element_Id != null) {
								element_page_id = resume_element_Id;//added by Vijaya
							}
							/*}
							else
							{
								SPKManage.setCtrlAltCFlag(false);
								SPKManage.setCtrlAltCFlag(SRState.login_id, SRState.curTestSetId, interview_idPre,SRState.apply_id, SRState.year, SRState.kaiji,false);
							}	*/
						}
						mgr.showPage(`Exam-${interview_id}-${element_page_id}`);
						if (i - 1 != 0) {
							var interviewPre = exam_pages[i - 1].interview;
							var interview_idPre = interviewPre.interview_id;
							SPKManage.setAnsweredInterviewId(SRState.login_id, SRState.curTestSetId, interview_idPre, SRState.apply_id, SRState.year, SRState.kaiji);
						}
						SPKManage.normalizeTempFiles(SRState.login_id);
						break;
					}
				}
			}
			return;
		}

		jquery$1('main').css('visibility', 'visible');
		jquery$1('[name="userId1"]').focus();
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageLogin pageWillDisappear(mgr) {');
		SPKManage.writeLog('page will disappear: Login');

		jquery$1('#js-sr-login').off('submit');
	}
};

// 説明画面1
var PageDescription1 = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageDescription1 pageWillAppear(mgr) {');
		SPKManage.writeLog('page will appear: Description1');

		var booklet_name = SRState.curTestSetId;
		var booklet_manager = new BookletManager();
		if (!booklet_manager.load(booklet_name)) {
			throw new Error('試験データの読み込みに失敗しました')
		}

		var test_set = booklet_manager.testset;
		var decrypted_dir = '../../../tmp/';
		var data_dir = '../../../data/';

		var decrypted_desc_movie_1 = '';

		if (test_set['test_desc_movie_1']) {
			var desc_movie_1 = test_set['test_desc_movie_1'];
			//Code Change By Abhijeet -- CBT#189 changes to decrypt file  -- START
			SPKManage.decryptDataFile(`spk/${booklet_name}/${desc_movie_1}`);
			decrypted_desc_movie_1 = decrypted_dir + desc_movie_1;
			//TO USE Unecrypted File Uncomment following line and comment above 2 LINE
			///decrypted_desc_movie_1 = data_dir + `spk/${booklet_name}/${desc_movie_1}`;
			//Code Change By Abhijeet -- CBT#189 changes to decrypt file  -- END
		}

		return mgr.renderTemplate('s_section_start.html',
			{
				test_time: test_set.test_time,
				test_num: test_set.test_num,
				section_list: SPKManage.mkSectionList(),
				desc_movie_1: decrypted_desc_movie_1,
				student_grade: (SPKManage.getCurStudentGrade() != null && SPKManage.getCurStudentGrade() != "") ? (SPKManage.getCurStudentGrade() == "20" ? "2級" : (SPKManage.getCurStudentGrade() == "25" ? "準2級" : (SPKManage.getCurStudentGrade() == "23" ? "準2級プラス" : (SPKManage.getCurStudentGrade() == "30" ? "3級" : (SPKManage.getCurStudentGrade() == "15" ? "準1級" : ""))))) : "",
				exam_expire_year: '',
				exam_expire_month: '',
				exam_expire_day: '',
				/*exam_expire_year: SPKManage.getCurExamEndDate()!=null && SPKManage.getCurExamEndDate()!='' ? SPKManage.getCurExamEndDate().split("-")[0] : '',
				exam_expire_month: SPKManage.getCurExamEndDate()!=null && SPKManage.getCurExamEndDate()!='' ? SPKManage.getCurExamEndDate().split("-")[1] : '',
				exam_expire_day: SPKManage.getCurExamEndDate()!=null && SPKManage.getCurExamEndDate()!='' ? SPKManage.getCurExamEndDate().split("-")[2] : '',*/
			}
		)
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageDescription1 pageDidAppear(mgr) {');
		SPKManage.writeLog('page did appear: Description1');

		var booklet_name = SRState.curTestSetId;
		var booklet_manager = new BookletManager();
		if (!booklet_manager.load(booklet_name)) {
			throw new Error('試験データの読み込みに失敗しました')
		}

		var test_set = booklet_manager.testset;
		var decrypted_dir = '../../../tmp/';
		var data_dir = '../../../data/';
		var decrypted_desc_movie_1 = '';
		var systemType = SPKManage.getSystemType();
		if (systemType == "scbt" && systemType != "" && systemType != "NULL") {
			jquery$1('#headerImage').html('<img src="../img/frame/logo_SCBT.png" alt="英検CBT　スピーキングテスト">');
		}
		else {
			jquery$1('#headerImage').html('<img src="../img/frame/logo_ECBT.png" alt="英検CBT　スピーキングテスト">');
		}
		/*if (test_set['test_desc_movie_1']) {
			var desc_movie_1 = test_set['test_desc_movie_1'];
			decrypted_desc_movie_1 = decrypted_dir + desc_movie_1;
		}*/

		//音量調整スライダー
		//setupVolumeSlider();

		//var status_code = "S002"; //Added by Vijaya EIK-18 -> Description1 status 
		//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, "", status_code, false);//added by Vijaya EIK-18

		var next_ok = false;

		/*set_video_event_handler(jquery$1('#js-desc-video'), test_set['test_desc_movie_1']);
		set_video_retry_handler(jquery$1('#js-desc-video'), test_set['test_desc_movie_1'], decrypted_desc_movie_1);*/

		/*jquery$1('#js-desc-video')
			.on('canplaythrough', wrap_log(function (event) {
				SPKManage.writeLog(`start playing: ${test_set['test_desc_movie_1']}`);
				event.target.play();
			}))
			.each(function (idx, elem) {
				setTimeout(function () {
					elem.src = decrypted_desc_movie_1;
					elem.load();
				}, 1000);
			});*/

		//jquery$1('#js-desc-video').on('ended', wrap_log(function (event) {
		//jquery$1('#js-button-state').removeClass('disabled');
		next_ok = true;
		//}));

		jquery$1('#js-next-button').on('click', wrap_log(function (event) {
			if (next_ok) {
				mgr.showPage('TestBeforeCheck');
			}
			return false;
		}));

	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageDescription1 pageWillDisappear(mgr) {');
		SPKManage.writeLog('page will disappear: Description1');

		jquery$1('#js-next-button').off('click');
	}
};


//Step guide part One
var PageStepGuidePartOne = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('page will appear: StepGuidePartOne');

		return mgr.renderTemplate('step_guide_part_one.html', {
		})
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('page did appear: StepGuidePartOne');

		jquery$1('#js-next-button').on('click', wrap_log(function (event) {
			mgr.showPage('TestBeforeCheck');
			return false;
		}));
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('page will disappear: StepGuidePartOne');
	}
};

// テスト前のチェック画面
var PageTestBeforeCheck = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageTestBeforeCheck pageWillAppear(mgr) {');
		SPKManage.writeLog('page will appear: TestBeforeCheck');

		return mgr.renderTemplate('test_before_check.html', {
		})
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageTestBeforeCheck pageDidAppear(mgr) {');
		SPKManage.writeLog('page did appear: TestBeforeCheck');

		//var status_code = "S003"; //Added by Vijaya EIK-18 -> TestBeforeCheck status 
		//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, "", status_code, false);//added by Vijaya EIK-18

		jquery$1('#js-next-button').on('click', wrap_log(function (event) {
			mgr.showPage('SoundCheck');
			return false;
		}));
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageTestBeforeCheck pageWillDisappear(mgr) {');
		SPKManage.writeLog('page will disappear: TestBeforeCheck');
	}
};

// 聴き取り音声の音量チェック画面
var PageSoundCheck = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageSoundCheck pageWillAppear(mgr) {');
		SPKManage.writeLog('page will appear: SoundCheck');

		return mgr.renderTemplate('test_check.html', {
		})
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageSoundCheck pageDidAppear(mgr) {');
		SPKManage.writeLog('page did appear: SoundCheck');

		//var status_code = "S004"; //Added by Vijaya EIK-18 -> SoundCheck status 
		//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, "", status_code, false);//added by Vijaya EIK-18

		setupVolumeSlider();

		jquery$1('#js-next-button').on('click', wrap_log(function (event) {
			mgr.showPage('RecordCheck');
			return false;
		}));
		class PauseExam {
			constructor() {
				var this$1 = this;

				this.keycombo = 'ctrl+alt+s';
				this.buttonHandler = wrap_log(function () {
					var pw = jquery$1('#stopPassWord').val();
					if (SPKManage.chkAdmPassword(pw)) {
						this$1.leave();
					} else {
						jquery$1('.stoppage .passerror').show();
					}
				});
			}

			bind() {
				var this$1 = this;

				mousetrap.bind(this.keycombo, wrap_log(function (e) {
					this$1.enter();
				}));
			}

			unbind() {
				mousetrap.unbind(this.keycombo);
			}

			enter() {

				this.unbind();

				jquery$1('#sound').each(function () {
					this.pause(); // Stop playing
				});

				SPKManage.writeLog('Pause exam');
				showSpkBodyModal('stoppage');
				jquery$1('#stopPassWord').val("");
				jquery$1('.stoppage .passerror').hide();
				jquery$1('#js-ret-pause').on('click', this.buttonHandler);
			}

			leave() {
				SPKManage.writeLog('Resume exam');

				hideSpkBodyModal('stoppage', true);
				jquery$1('#js-ret-pause').off('click', this.buttonHandler);
				this.bind();
				mgr.showPage('SoundCheck');
			}
		}
		var pause_exam = new PauseExam();
		pause_exam.bind();
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageSoundCheck pageWillDisappear(mgr) {');
		SPKManage.writeLog('page will disappear: SoundCheck');
	}
};


// マイクの確認画面
var PageMicCheck = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageMicCheck pageWillAppear(mgr) {');
		SPKManage.writeLog('page will appear: MicCheck');

		return mgr.renderTemplate('microphone_check.html', {
		})
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageMicCheck pageDidAppear(mgr) {');
		SPKManage.writeLog('page did appear: MicCheck');

		var rec_level_ok = false;

		var $rec_volume = jquery$1('#js-rec-volume');
		if ($rec_volume.length > 0) {
			$rec_volume.slider({
				tooltip: 'hide',
				value: SRState.rec_volume,
			});

			$rec_volume.on('change', wrap_log(function (event) {
				SRState.rec_volume = event.value.newValue;
				setState(SRState);
			}));
		}


		jquery$1('#js-next-button').on('click', wrap_log(function (event) {
			if (rec_level_ok) {
				mgr.showPage('RecordCheck');
			}
			return false;
		}));

		var rec_level_elem = jquery$1('#js-level-meter').get(0);
		setupRecordLevelMeter({
			is_recording() {
				return true
			},
			get_target() {
				return rec_level_elem
			},
			get_level() {
				var lv = SPKManage.getMicCheckLevel(SRState.rec_volume);

				if (lv >= SPKManage.getMicCheckOKLevel() && !rec_level_ok) {
					//jquery$1('#js-rec-level-ok').removeClass('disabled');
					jquery$1('#js-next-button').removeClass('disabled');
					jquery$1('#js-next-button').removeClass('nocursor');
					rec_level_ok = true;
				}

				return lv
			},
		});

		SPKManage.startMicCheck();
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageMicCheck pageWillDisappear(mgr) {');
		SPKManage.writeLog('page will disappear: MicCheck');

		SPKManage.stopMicCheck();
	}
};

// 録音の確認画面
var PageRecordCheck = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageRecordCheck pageWillAppear(mgr) {');
		SPKManage.writeLog('page will appear: RecordCheck');

		return mgr.renderTemplate('recording_check.html', {
		})
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageRecordCheck pageDidAppear(mgr) {');
		SPKManage.writeLog('page did appear: RecordCheck');
		var rec_level_ok = false;
		var checkPage = "";
		var t;
		setupVolumeSlider();

		//var status_code = "S005"; //Added by Vijaya EIK-18 -> RecordCheck status 
		//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, "", status_code, false);//added by Vijaya EIK-18

		var rec_level_elem = jquery$1('#js-level-meter').get(0);
		setupRecordLevelMeter({
			is_recording() {
				return SPKManage.isRecording()
			},
			get_target() {
				return rec_level_elem
			},
			get_level() {
				//code modified to check mike's volumes crosses the default value
				var lv = SPKManage.getRecordLevel(SRState.rec_volume);
				SPKManage.setEvnRecordCheckPage(true);
				if (lv >= SPKManage.getMicCheckOKLevel() && !rec_level_ok) {
					rec_level_ok = true;
				}
				return lv
			},
		});

		//
		var record_timer = null;
		var start_record = function () {
			if (record_timer) {
				record_timer.stop();
				record_timer = null;
			}

			t = new RecordTimer(10);
			record_timer = t;
			//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- START
			var sysDate = new Date();
			var recStartSysTime = sysDate.getTime();
			var recStartDuration = -1;
			//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- END
			t.onTick(wrap_log(function () {
				checkPage = "record";
				var sec = t.getSecond();
				//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- START
				var sysDateInRec = new Date();
				var sysTimeInRec = sysDateInRec.getTime();
				SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> sysTimeInRec::' + sysTimeInRec + ' recStartSysTime[' + recStartSysTime + '] sec[' + sec + '] recStartDuration[' + recStartDuration + ']');
				if (((sysTimeInRec - recStartSysTime) / 1000) >= 6) {
					if (sec == recStartDuration) {
						SPKManage.writeLog("Error In Audio Recording [RecordCheck]");
						throw new Error('ErrorInRecProgress');
					} else {
						recStartSysTime = sysTimeInRec;
						recStartDuration = sec;
					}
				}
				//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- END
				if (t.isStarted()) {
					jquery$1('#js-countdown-timer').text('残り時間  ' + t.text());
				}

				if (SPKManage.getRecordingProgress() > 0.0) {
					jquery$1('#js-preparing').hide();
					jquery$1('#js-preparing-msg').hide();
					jquery$1('#js-recording').show();
					jquery$1('#js-recording-msg').show();
					jquery$1('#js-playing').hide();
					jquery$1('#js-playing-msg').hide();
					jquery$1('#js-rec-info').css('display', 'block');
				}

				if (sec == 0) {
					t.stop();
				}

				return (sec != 0)
			}));

			t.onStop(wrap_log(function () {
				jquery$1('#js-preparing').show();
				jquery$1('#js-preparing-msg').hide();
				jquery$1('#js-recording').hide();
				jquery$1('#js-recording-msg').show();
				jquery$1('#js-playing').hide();
				jquery$1('#js-playing-msg').hide();

				var ended_alarm = false;
				var ended_conv_mp3 = false;

				jquery$1('#js-end-beep').get(0).play();
				jquery$1('#js-end-beep').on('ended', wrap_log(function (event) {
					ended_alarm = true;
				}));

				var fname = 'rec_test_' + SRState.login_id;
				var wavPath = `cbt/testResults/spkexam/wav/${fname}_${SRState.prev_page_visited}.wav`;
				var mp3Path = `cbt/applog/${fname}.mp3`;

				setTimeout(wrap_log(function () {
					if (SPKManage.saveRecordedSound(wavPath)) {
						SPKManage.convertMP3(wavPath, mp3Path, SRState.login_id, SRState.curTestSetId, '', SRState.apply_id, SRState.year, SRState.kaiji, '', '');
					}

					var conv_proc = function () {
						if (!SPKManage.isConvertingMP3()) {
							ended_conv_mp3 = true;
							return
						}
						setTimeout(wrap_log(conv_proc), 100);
					};
					conv_proc();
				}), 10);

				var wait_proc = function () {
					if (ended_conv_mp3 && ended_alarm) {
						var exit_value = SPKManage.getConvMP3Result();

						SPKManage.writeLog(`getConvMP3Result: ${exit_value}`);

						if (exit_value != 0) {
							SPKManage.writeLog(`convert mp3 process result: ${exit_value}`);
							throw new Error('音声の保存に失敗しました');
						}

						checkMP3FileSize(mp3Path);

						play();
						return
					}
					setTimeout(wrap_log(wait_proc), 100);
				};
				wait_proc();

			}));

			jquery$1('#js-start-beep').get(0).play();
			jquery$1('#js-start-beep').on('ended', wrap_log(function (event) {
				checkPage = "record";
				t.start();
			}));

		};

		var play = function () {
			SPKManage.writeLog('rec_level_ok ' + rec_level_ok);
			if (rec_level_ok) {
				jquery$1('#js-preparing').hide();
				jquery$1('#js-preparing-msg').hide();
				jquery$1('#js-recording').hide();
				jquery$1('#js-recording-msg').hide();
				jquery$1('#js-playing').show();
				jquery$1('#js-playing-msg').show();
				jquery$1('#js-rec-info').css('display', 'none');
				jquery$1('#js-buttons').css('display', 'block');
				jquery$1('#js-backbutton').css('display', 'none');
				jquery$1('#rec-fail-msg').css('display', 'none');

				jquery$1('#js-recorded-mp3')
					.on('canplaythrough', wrap_log(function (event) {
						event.target.play();
						checkPage = "recordPlay";
					}))
					.each(function (idx, elem) {
						elem.src = '../../../applog/rec_test_' + SRState.login_id + '.mp3';
						elem.load();
					});
			} else { //added else part to show error message if recording fails due to failuer microphone
				jquery$1('#js-preparing').hide();
				jquery$1('#js-preparing-msg').hide();
				jquery$1('#js-recording').show();
				jquery$1('#js-recording-msg').show();
				jquery$1('#js-playing').hide();
				jquery$1('#js-playing-msg').hide();
				jquery$1('#js-rec-info').css('display', 'none');
				jquery$1('#js-buttons').css('display', 'none');
				jquery$1('#js-backbutton').css('display', 'block');
				jquery$1('#rec-fail-msg').css('display', 'block');
			}
		};

		//
		var skip_rec_check_desc = SRState.skip_rec_check_desc;
		var wait_timer;
		if (skip_rec_check_desc) {
			start_record();
		} else {
			SRState.skip_rec_check_desc = true;
			setState(SRState);

			wait_timer = new CountDownTimer(5);

			wait_timer.onTick(wrap_log(function () {
				var sec = wait_timer.getSecond();
				checkPage = "prerecord";
				if (wait_timer.isStarted()) {
					jquery$1('#js-countdown-timer').text('残り時間  ' + wait_timer.text());
				}

				if (sec == 0) {
					wait_timer.stop();
				}

				return (sec != 0)
			}));

			wait_timer.onStop(wrap_log(function () {
				start_record();
			}));

			wait_timer.start();
		}


		jquery$1('#js-next-button').on('click', wrap_log(function (event) {
			mgr.showPage('Description2');
			return false;
		}));

		jquery$1('#js-back-button').on('click', wrap_log(function (event) {
			mgr.showPage('RecordCheck');
			return false;
		}));

		jquery$1('#js-prev-button').on('click', wrap_log(function (event) {
			mgr.showPage('RecordCheck');
			return false;
		}));
		class PauseExam {
			constructor() {
				var this$1 = this;

				this.keycombo = 'ctrl+alt+s';
				this.buttonHandler = wrap_log(function () {
					var pw = jquery$1('#stopPassWord').val();
					if (SPKManage.chkAdmPassword(pw)) {
						this$1.leave();
					} else {
						jquery$1('.stoppage .passerror').show();
					}
				});
			}

			bind() {
				var this$1 = this;

				mousetrap.bind(this.keycombo, wrap_log(function (e) {
					this$1.enter();
				}));
			}

			unbind() {
				mousetrap.unbind(this.keycombo);
			}

			enter() {

				this.unbind();

				if (checkPage === "record") {
					jquery$1('#js-rerecord-msg').html('<br>音声は再録音となります。');
					t.pause();
					terminated = true;
				}
				else if (checkPage === "prerecord") {
					wait_timer.pause();
					terminated = true;
				}
				else {
					jquery$1('#js-recorded-mp3').each(function () {
						this.pause(); // Stop playing
					});
					checkPage = "";
				}
				SPKManage.writeLog('Pause exam');
				showSpkBodyModal('stoppage');
				jquery$1('#stopPassWord').val("");
				jquery$1('.stoppage .passerror').hide();
				jquery$1('#js-ret-pause').on('click', this.buttonHandler);
			}

			leave() {
				SPKManage.writeLog('Resume exam');

				var skipRestore = (checkPage === "record" || checkPage === "prerecord");
				hideSpkBodyModal('stoppage', skipRestore);
				jquery$1('#js-ret-pause').off('click', this.buttonHandler);
				this.bind();
				if (checkPage === "record" || checkPage === "prerecord") {
					mgr.showPage('RecordCheck');
					checkPage = "";
				}
				else {
					jquery$1('#js-preparing').hide();
					jquery$1('#js-preparing-msg').hide();
					jquery$1('#js-recording').hide();
					jquery$1('#js-recording-msg').hide();
					jquery$1('#js-playing').show();
					jquery$1('#js-playing-msg').show();
					jquery$1('#js-rec-info').css('display', 'none');
					jquery$1('#js-buttons').css('display', 'block');
					jquery$1('#js-backbutton').css('display', 'none');
					jquery$1('#rec-fail-msg').css('display', 'none');

					jquery$1('#js-recorded-mp3')
						.on('canplaythrough', wrap_log(function (event) {
							event.target.play();
						}))
						.each(function (idx, elem) {
							elem.src = '../../../applog/rec_test_' + SRState.login_id + '.mp3';
							elem.load();
						});
					checkPage = "";
				}
			}
		}
		var pause_exam = new PauseExam();
		pause_exam.bind();
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageRecordCheck pageWillDisappear(mgr) {');
		SPKManage.writeLog('page will disappear: RecordCheck');
	}
};

//Step guide part two
var PageStepGuidePartTwo = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('page will appear: StepGuidePartTwo');

		return mgr.renderTemplate('step_guide_part_two.html', {
		})
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('page did appear: StepGuidePartTwo');

		jquery$1('#js-next-button').on('click', wrap_log(function (event) {
			mgr.showPage('StepGuidePartThree');
			return false;
		}));
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('page will disappear: StepGuidePartTwo');
	}
};

//Step guide part three
var PageStepGuidePartThree = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('page will appear: StepGuidePartThree');

		return mgr.renderTemplate('step_guide_part_three.html', {
		})
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('page did appear: StepGuidePartThree');

		jquery$1('#js-next-button').on('click', wrap_log(function (event) {
			mgr.showPage('Description2');
			return false;
		}));
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('page will disappear: StepGuidePartThree');
	}
};


// 説明画面2
var PageDescription2 = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageDescription2 pageWillAppear(mgr) {');
		SPKManage.writeLog('page will appear: Description2');

		var booklet_name = SRState.curTestSetId;
		var booklet_manager = new BookletManager();
		if (!booklet_manager.load(booklet_name)) {
			throw new Error('試験データの読み込みに失敗しました')
		}

		var test_set = booklet_manager.testset;
		var decrypted_dir = '../../../tmp/';

		var decrypted_desc_movie_2 = '';
		var decrypted_desc_image = '';
		var data_dir = '../../../data/';

		if (test_set['test_desc_movie_2']) {
			var desc_movie_2 = test_set['test_desc_movie_2'];
			//Code Change By Abhijeet -- CBT#189 changes to decrypt file  -- START
			SPKManage.decryptDataFile(`spk/${booklet_name}/${desc_movie_2}`);
			decrypted_desc_movie_2 = decrypted_dir + desc_movie_2;
			//TO USE Unecrypted File Uncomment this line and comment above 2 LINE
			//decrypted_desc_movie_2 = data_dir + 'spk/${booklet_name}/${desc_movie_2}';			
			//Code Change By Abhijeet -- CBT#189 changes to decrypt file  -- END
		}

		if (test_set['test_desc_image']) {
			var desc_image = test_set['test_desc_image'];
			//Code Change By Abhijeet -- CBT#189 changes to decrypt file  -- START
			SPKManage.decryptDataFile(`spk/${booklet_name}/${desc_image}`);
			decrypted_desc_image = decrypted_dir + desc_image;
			//TO USE Unecrypted File Uncomment this line and comment above 2 LINE
			//decrypted_desc_image = data_dir + `spk/${booklet_name}/${desc_image}`;			
			//Code Change By Abhijeet -- CBT#189 changes to decrypt file  -- START
		}

		return mgr.renderTemplate('s_section_summary.html',
			test_set,
			{
				section_list: SPKManage.mkSectionList(),
				desc_movie_2: decrypted_desc_movie_2,
				desc_image: decrypted_desc_image,
			}
		)
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageDescription2 pageDidAppear(mgr) {');
		SPKManage.writeLog('page did appear: Description2');

		/*setupVolumeSlider();*/

		var booklet_name = SRState.curTestSetId;
		var booklet_manager = new BookletManager();
		if (!booklet_manager.load(booklet_name)) {
			throw new Error('試験データの読み込みに失敗しました')
		}

		var status_code = "S006"; //Added by Vijaya EIK-18 -> Description2 status 
		SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, "", status_code, false);//added by Vijaya EIK-18

		var test_set = booklet_manager.testset;
		var decrypted_dir = '../../../tmp/';
		var test_desc_duration = parseInt(test_set.test_desc_duration, 10) || 0;
		var timer = new CountDownTimer(test_desc_duration);
		var next_ok = false;

		var decrypted_desc_movie_2 = '';
		if (test_set['test_desc_movie_2']) {
			var desc_movie_2 = test_set['test_desc_movie_2'];
			decrypted_desc_movie_2 = decrypted_dir + desc_movie_2;
		}

		if (decrypted_desc_movie_2 && jquery$1('#js-desc-video').length) {
			bindNativeVideoPlayback(jquery$1('#js-desc-video'), test_set['test_desc_movie_2'], decrypted_desc_movie_2, { autoPrepare: true });
			registerNativeVideoHandler(function (event, data) {
				if (event === 'ready' && data && data.fname === test_set['test_desc_movie_2']) {
					syncNativeVideoBounds(jquery$1('#js-desc-video').get(0));
					applyNativeVideoVolumeFromState();
					SPKManage.playNativeVideo();
				}
			});
		}

		/*set_video_event_handler(jquery$1('#js-desc-video'), test_set['test_desc_movie_2']);
		set_video_retry_handler(jquery$1('#js-desc-video'), test_set['test_desc_movie_2'], decrypted_desc_movie_2);

		jquery$1('#js-desc-video')
			.on('canplaythrough', wrap_log(function (event) {
				SPKManage.writeLog(`start playing: ${test_set['test_desc_movie_2']}`);
				event.target.play();

				timer.start();
			}))
			.each(function (idx, elem) {
				setTimeout(function () {
					elem.src = decrypted_desc_movie_2;
					elem.load();
				}, 1000);
			});*/

		/*timer.onTick(wrap_log(function () {
			var sec = timer.getSecond();

			jquery$1('#js-countdown-timer').text(timer.text());

			if (sec == 0) {
				timer.stop();
				jquery$1('#js-button-state').removeClass('disabled');
				next_ok = true;
				// mgr.showPage(name_next_exam)
				return false
			}
			return true
		}));*/

		jquery$1('#js-next-button').on('click', wrap_log(function (event) {
			//if (next_ok) {
			mgr.showPage(SRState.first_exam_page);
			//}
			return false;
		}));

		//timer.start();		
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageDescription2 pageWillDisappear(mgr) {');
		SPKManage.writeLog('page will disappear: Description2');
	}
};

// SR 終了画面
var PageEndExam = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageEndExam pageWillAppear(mgr) {');
		SPKManage.writeLog('page will appear: EndExam');

		return mgr.renderTemplate('end_exam.html', {
		})
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageEndExam pageDidAppear(mgr) {');
		SPKManage.writeLog('page did appear: EndExam');

		var status_code = "S007"; //Added by Vijaya EIK-18 -> EndExam status 
		SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, "", status_code, false);//added by Vijaya EIK-18

		SPKManage.incTestsetId();
		SPKManage.redirect(SPKManage.getCompTransUrl());
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageEndExam pageWillDisappear(mgr) {');
		SPKManage.writeLog('page will disappear: EndExam');
	}
};

// 端末状態確認画面
var PageTermState = {
	pageWillAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageTermState pageWillAppear(mgr) {');
		SPKManage.writeLog('page will appear: TermState');
		SPKManage.termStateLogWrite(' TermStateLogManage page will appear: TermState');

		return mgr.renderTemplate('termstate.html', {
			Display: SPKManage.getDisplay(),
			UserAgent: SPKManage.getUserAgent(),
		})
	},

	pageDidAppear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageTermState pageDidAppear(mgr) {');
		SPKManage.writeLog('page did appear: TermState');
		SPKManage.termStateLogWrite('TermStateLogManage page did appear: TermState');
		// setupVolumeSlider()

		if (!SPKManage.checkMicAvalable()) {
			jquery$1('#js-start-record').prop('disabled', true);
			jquery$1('#js-no-mic-msg').show();
		}

		var status_code = "S008"; //Added by Vijaya EIK-18 -> TermState status
		SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, "", status_code, false);//added by Vijaya EIK-18

		var timer;
		var file_name;
		jquery$1('#js-to-top-button').on('click', wrap_log(function (event) {
			SPKManage.stopRecordSound(100);

			var SUrl = SPKManage.getSUrl();
			SPKManage.redirect(SUrl);
			return false;
		}));

		var rec_level_elem = jquery$1('#js-level-meter').get(0);
		setupRecordLevelMeter({
			is_recording() {
				return SPKManage.isRecording()
			},
			get_target() {
				return rec_level_elem
			},
			get_level() {
				return SPKManage.getRecordLevel(SRState.rec_volume)
			},
		});

		jquery$1('#js-start-record').on('click', wrap_log(function (event) {
			SPKManage.sendSRCheckStatus("T013", null);   //Added by sharad for EIK#1207
			/*if (timer) {
				timer.stop();
			}*/
			SPKManage.termStateLogWrite('TermStateLogManage Recording started: TermState');
			jquery$1('#js-start-record').prop('disabled', true);
			jquery$1('#js-stop-record').prop('disabled', false);
			jquery$1('#js-start-play').prop('disabled', true);
			jquery$1('#js-stop-play').prop('disabled', true);
			jquery$1('#js-preparing').hide();
			jquery$1('#js-recording').show();
			jquery$1('#js-playing').hide();

			timer = new RecordTimer(10);

			timer.onTick(wrap_log(function () {
				var sec = timer.getSecond();

				if (timer.isStarted()) {
					jquery$1('#js-countdown-timer').text('残り時間  ' + timer.text());
				}

				if (sec == 0) {
					SPKManage.termStateLogWrite('TermStateLogManage Recording timer ended: TermState');
					timer.stop();
					jquery$1(".container").hide();
					jquery$1('#loading-image1').css("display", "block");
					var interval = setInterval(function () {
						var result = SPKManage.isSRCheckStatusThreadCompleted();
						if (result) {
							clearInterval(interval);
							jquery$1('#loading-image1').css("display", "none");
							jquery$1(".container").show();
							result = false;
						}
					}, 3000);
				}
				return (sec != 0)
			}));

			timer.onStop(wrap_log(function () {
				SPKManage.termStateLogWrite('timer.onStop start ');
				jquery$1('#js-start-record').val('録音開始');

				jquery$1('#js-preparing').show();
				jquery$1('#js-recording').hide();
				jquery$1('#js-playing').hide();

				jquery$1('#js-start-record').prop('disabled', true);
				jquery$1('#js-stop-record').prop('disabled', true);
				jquery$1('#js-start-play').prop('disabled', true);
				jquery$1('#js-stop-play').prop('disabled', true);

				jquery$1('#js-countdown-timer').text('残り時間  -- : --');

				var ended_conv_mp3 = false;
				file_name = SPKManage.getTermStateFileName();
				SPKManage.termStateLogWrite('File name with Mic id ' + file_name);
				var fname = file_name;
				var wavPath = `cbt/tmp/${fname}.wav`;
				var mp3Path = `cbt/applog/${fname}.mp3`;

				setTimeout(wrap_log(function () {
					if (SPKManage.saveRecordedSound(wavPath)) {
						SPKManage.convertMP3(wavPath, mp3Path, SRState.login_id, SRState.curTestSetId, '', SRState.apply_id, SRState.year, SRState.kaiji, '', '');
					}

					var conv_proc = wrap_log(function () {
						if (!SPKManage.isConvertingMP3()) {
							ended_conv_mp3 = true;
							SPKManage.termStateLogWrite('Mp3 conversion done : ' + file_name);
							SPKManage.isUploadTermStateMp3();	// Added by Atish to start upload after MP3 conversion done #EIK-3130 
							return
						}
						setTimeout(conv_proc, 100);
					});
					conv_proc();
				}), 10);

				var wait_proc = wrap_log(function () {
					if (ended_conv_mp3) {
						var exit_value = SPKManage.getConvMP3Result();
						if (exit_value != 0) {
							SPKManage.termStateLogWrite(`convert mp3 process result: ${exit_value}`);
							throw new Error('音声の保存に失敗しました')
						}

						checkMP3FileSize(mp3Path);

						jquery$1('#js-start-play').prop('disabled', false);
						jquery$1('#js-start-record').prop('disabled', false);

						return
					}
					setTimeout(wait_proc, 200);
				});
				wait_proc();
				SPKManage.termStateLogWrite('timer.onStop end ');
			}));

			timer.start();

			return false;
		}));

		jquery$1('#js-stop-record').on('click', wrap_log(function (event) {
			if (timer) {
				SPKManage.termStateLogWrite('TermStateLogManage Recording timer ended: TermState js-stop-record click');
				timer.stop();
				jquery$1(".container").hide();
				jquery$1('#loading-image1').css("display", "block");
				var interval1 = setInterval(function () {
					var result = SPKManage.isSRCheckStatusThreadCompleted();
					if (result) {
						clearInterval(interval1);
						jquery$1('#loading-image1').css("display", "none");
						jquery$1(".container").show();
						result = false;
					}
				}, 3000);
			}
			return false;
		}));

		jquery$1('#js-start-play').on('click', wrap_log(function (event) {
			SPKManage.sendSRCheckStatus("T015", null); //Added by sharad for EIK#1207
			jquery$1('#js-recorded-mp3').attr('src', '../../../applog/' + file_name + '.mp3')
				.on('canplaythrough', wrap_log(function (event) {
					event.target.play();
					jquery$1('#js-stop-play').prop('disabled', false);
				}))
				.on('ended', wrap_log(function () {
					jquery$1('#js-stop-play').prop('disabled', true);
				}));

			return false;
		}));

		jquery$1('#videoPlay').on('click', wrap_log(function (event) {
			SPKManage.sendSRCheckStatus("T016", null);  //Added by sharad for EIK#1207
		}));

		jquery$1('#js-stop-play').on('click', wrap_log(function (event) {
			jquery$1('#js-recorded-mp3').get(0).pause();
			return false;
		}));
	},

	pageWillDisappear(mgr) {
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> PageTermState pageWillDisappear(mgr) {');
		SPKManage.writeLog('page will disappear: TermState');
	}
};



// 出題画面の作成
function createExamPages(pageManager) {
	SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> function createExamPages(pageManager) {');
	var booklet_name = SRState.curTestSetId;

	var booklet_manager = new BookletManager();
	if (!booklet_manager.load(booklet_name)) {
		throw new Error('試験データの読み込みに失敗しました')
	}

	var interviews = booklet_manager.getInterviews();

	var exam_pages = [];
	{
		for (var i = 0; i < interviews.length; ++i) {
			var interview = interviews[i];
			var interview_id = interview.interview_id;
			var elements = booklet_manager.getScriptElements(interview_id);
			for (var j = 0; j < elements.length; ++j) {
				var element = elements[j];
				var element_page_id = element.element_page_id;
				exam_pages.push({
					interview: interview,
					element: element,
					needs_set_resume: j == elements.length - 1,
				});
			}
		}
	}

	var loop = function (i) {
		var is_yes_no_page = 0;
		SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> var loop = function ( i ) {');
		var interview$1 = exam_pages[i].interview;
		var element$1 = exam_pages[i].element;
		var needs_set_resume = exam_pages[i].needs_set_resume;


		var interview_id_prev$1 = '';
		if (i > 0) {
			interview_id_prev$1 = (interview$1.interview_id) - 1;
		} else {
			interview_id_prev$1 = interview$1.interview_id;
		}

		var interview_id$1 = interview$1.interview_id;
		var element_page_id$1 = element$1.element_page_id;
		var audio_filename = element$1.audio_filename;
		var images = element$1.images;
		var template_type = element$1.template_type;
		var card_title = element$1.card_title;
		var card_text = element$1.card_text;
		//2018/01/08 Added by Kailas - #50 (On click of Yes/No button corresponing question will be loaded.) - Start
		var yes_element_page_id = interview$1.yes_element_page_id;
		var no_element_page_id = interview$1.no_element_page_id;
		var record_element_page_id = interview$1.record_element_page_id;
		//2018/01/08 Added by Kailas - #50 (On click of Yes/No button corresponing question will be loaded.) - End


		var element_dir = `../if/testset/${booklet_name}/${interview_id$1}/`;
		var decrypted_dir = '../../../tmp/';
		var data_dir = '../../../data/';

		var name_exam = `Exam-${interview_id$1}-${element_page_id$1}`;
		var name_next_exam = 'EndExam';
		if (i + 1 < exam_pages.length) {
			var next = exam_pages[i + 1];
			name_next_exam = `Exam-${next.interview.interview_id}-${next.element.element_page_id}`;
		}

		//2018/01/08 Added by Kailas - #50 (On click of Yes/No button corresponing question will be loaded.) - Start
		//2018/01/10 Added/Modified by Kailas - #164 ("Yes / No" type of the question may have multiple rows. So handle it accordingly.) - Start
		if (element$1.yes_no_type == '1') {
			//for loop to check whether next question ( interviewid) available, if available then set is_next_question_available to true and set next page name.
			var j = i + 1;
			var is_next_question_available = false;
			for (j; j < exam_pages.length; j++) {
				var next = exam_pages[j];
				if (next.interview.interview_id != interview$1.interview_id) {
					name_next_exam = `Exam-${next.interview.interview_id}-${next.element.element_page_id}`;
					SPKManage.writeLog(`name_next_exam: ${name_next_exam}`);
					is_next_question_available = true;
					break;
				}
			}
			//if next question not available then set next page as EndExam.
			if (is_next_question_available == false) {
				name_next_exam = 'EndExam';
				SPKManage.writeLog(`name_next_exam: ${name_next_exam}`);
			}

		}

		//build yes & no element pages ids array, if record_element_page_id != '' ( means if question is of yes no type).
		var name_next_exam_yes = [];
		var name_next_exam_no = [];
		var name_next_exam_record_page = '';
		if (record_element_page_id != '') {
			if (yes_element_page_id != '' && no_element_page_id != '') {
				name_next_exam_yes = yes_element_page_id.split(",");
				SPKManage.writeLog(`name_next_exam_yes array length: ` + name_next_exam_yes.length);
				var x;
				for (x = 0; x < name_next_exam_yes.length; x++) {
					name_next_exam_yes[x] = `Exam-${interview$1.interview_id}-` + name_next_exam_yes[x];
					SPKManage.writeLog(`name_next_exam_yes: ` + name_next_exam_yes[x]);
				}
				name_next_exam_no = no_element_page_id.split(",");
				SPKManage.writeLog(`name_next_exam_no array length: ` + name_next_exam_no.length);
				for (x = 0; x < name_next_exam_no.length; x++) {
					name_next_exam_no[x] = `Exam-${interview$1.interview_id}-` + name_next_exam_no[x];
					SPKManage.writeLog(`name_next_exam_no: ` + name_next_exam_no[x]);
				}
			}
			//set record page name.
			name_next_exam_record_page = `Exam-${interview$1.interview_id}-${record_element_page_id}`;
			SPKManage.writeLog(`name_next_exam_record_page: ${name_next_exam_record_page}`);
		}
		//2018/01/10 Added/Modified by Kailas - #164 ("Yes / No" type of the question may have multiple rows. So handle it accordingly.) - End
		//2018/01/08 Added by Kailas - #50 (On click of Yes/No button corresponing question will be loaded.) - End

		if (i == 0) {
			SRState.first_exam_page = name_exam;
		}

		var page_exam = {
			pageWillAppear(mgr) {
				SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> pageWillAppear(mgr) {');
				SPKManage.writeLog(`page will appear: ${interview_id$1}-${element_page_id$1}`);
				SPKManage.writeLog(`  record_file_name: ${interview$1.record_file_name}`);
				SPKManage.writeLog(`  part_no: ${interview$1.part_no}`);
				SPKManage.writeLog(`  part_name: ${interview$1.part_name}`);
				SPKManage.writeLog(`  is_recording: ${interview$1.is_recording}`);
				SPKManage.writeLog(`  type: ${element$1.type}`);
				SPKManage.writeLog(`  duration: ${element$1.duration}`);
				SPKManage.writeLog(`  audio_filename: ${element$1.audio_filename}`);
				SPKManage.writeLog(`  images: ${element$1.images}`);
				SPKManage.writeLog(`  alarm_sound: ${element$1.alarm_sound}`);
				SPKManage.writeLog(`  is_countdown: ${element$1.is_countdown}`);

				var breadcrumb = booklet_manager.getBreadcrumbInfo(interview_id$1, element_page_id$1);
				/*var sections = [];
				if (breadcrumb.section_max > 0) {
					for (var i = 1; i <= breadcrumb.section_max; ++i) {
						sections.push({
							str: i,
							current: (i == breadcrumb.section_now) ? 'class="current"' : '',
						});
					}
				}*/

				var task_name = breadcrumb.task_name;
				if (breadcrumb.task_max) {
					if (breadcrumb.task_now) {
						task_name += ' ' + breadcrumb.task_now;
					}
				}

				var task_states = [];
				var state_max = breadcrumb.state_max;
				var state_now = breadcrumb.state_now;
				var state_str = breadcrumb.state_str;
				if (state_max && state_str.length >= state_max) {
					for (var i$1 = 1; i$1 <= state_max; ++i$1) {
						task_states.push({
							number: i$1,
							str: state_str[i$1 - 1],
							current: (i$1 == state_now) ? 'class="current"' : '',
						});
					}
				}

				// データの復号
				var decrypted_audio_filename = '';
				var decrypted_images = '';
				/*var decrypted_mic_image = '';*/
				if (audio_filename) {
					//Code Change By Abhijeet -- CBT#189 changes to decrypt file  -- START
					SPKManage.decryptDataFile(`spk/${booklet_name}/${interview_id$1}/${audio_filename}`);
					decrypted_audio_filename = decrypted_dir + audio_filename;
					//TO USE Unecrypted File Uncomment following line and comment above 2 LINE
					//decrypted_audio_filename = data_dir + `spk/${booklet_name}/${interview_id$1}/${audio_filename}`;
					SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> decrypted_audio_filename: ' + decrypted_audio_filename);
					//Code Change By Abhijeet -- CBT#189 changes to decrypt file  -- END
				}

				if (images) {
					//Code Change By Abhijeet -- CBT#189 changes to decrypt file  -- START
					SPKManage.decryptDataFile(`spk/${booklet_name}/${interview_id$1}/${images}`);
					decrypted_images = decrypted_dir + images;
					//TO USE Unecrypted File Uncomment following line and comment above 2 LINE
					//decrypted_images = data_dir + `spk/${booklet_name}/${interview_id$1}/${images}`;
					SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> decrypted_images: ' + decrypted_images);
					//Code Change By Abhijeet -- CBT#189 changes to decrypt file  -- END
				}

				/*if (mic_image) {
					SPKManage.decryptDataFile(`spk/${booklet_name}/${interview_id$1}/${mic_image}`);
					decrypted_mic_image = decrypted_dir + mic_image;
				}*/

				var is_template_type_1 = false;
				var is_template_type_2 = false;

				if (template_type == 1) {
					SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> template_type == 1');
					is_template_type_1 = true;
				} else if (template_type == 2) {
					SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> template_type == 2');
					is_template_type_2 = true;
				}

				var showNextButton = false;
				//2018/01/08 Modified by Kailas - #52 (On click of Next button next question will be loaded.) - Start
				if ((element$1.type == 'record') && (name_next_exam != 'EndExam')) {
					showNextButton = true;
				}
				//2018/01/08 Modified by Kailas - #52 (On click of Next button next question will be loaded.) - End

				var showYesNoButtons = false;
				if (element$1.yes_no_type == "1") {
					is_yes_no_page = 1;
					showYesNoButtons = true;
				}

				return mgr.renderTemplate('exam.html', {
					audio_filename: decrypted_audio_filename,
					images: decrypted_images,
					is_template_type_1: is_template_type_1,
					is_template_type_2: is_template_type_2,
					card_title: card_title,
					card_text: card_text,
					showYesNoButtons: showYesNoButtons,
					/*sections: sections,*/
					showNextButton: showNextButton,
					task_name: task_name,
					task_max: breadcrumb.task_max,

					task_states: task_states,
					num_states: task_states.length,

					show_timer: element$1.is_countdown ? '' : 'style="visibility:hidden;"',
				})
			},

			pageDidAppear(mgr) {
				var enable_pause = false;
				checkType = "playback";
				var timer1;
				SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> pageDidAppear(mgr) {');
				SPKManage.writeLog('###prev_page_visited :' + SRState.prev_page_visited);
				SPKManage.writeLog(`page did appear: ${interview_id$1}-${element_page_id$1}`);
				var buttonYesNoClickFlag = false;

				setupVolumeSlider();
				SRState.replay_max_click_count = 0;
				hideReplayButton();

				var rec_level_elem = jquery$1('#js-level-meter').get(0);
				setupRecordLevelMeter({
					is_recording() {
						return SPKManage.isRecording()
					},
					get_target() {
						return rec_level_elem
					},
					get_level() {
						return SPKManage.getRecordLevel(SRState.rec_volume)
					},
				});

				jquery$1('#js-next-button').on('click', wrap_log(function (event) {
					jquery$1('#js-next-button').prop("disabled", true);
					if (!SPKManage.isMeargeException()) {

						mgr.showPage(name_next_exam);
					}
					return false;
				}));

				//if (element$1.type == 'playback' && audio_filename) {
				class SRVideo {
					constructor($video, fname, src, opt) {
						var this$1 = this;
						if (opt === void 0) opt = {};

						this.$video = $video;
						this.fname = fname;
						this.src = src;
						this.opt = Object.assign({}, {
							play() { },
							ended() { }
						}, opt);
						this._directPlayToken = 0;
						this._generation = 0;
						this._reinitCount = 0;
						this._videoFlowId = nextVideoFlowId();
						this._nativeMode = isNativeVideoBridgeEnabled();
						this._pendingNativeDirectPlay = false;
						this._nativeAwaitingReinitPlay = false;

						function makeFlowCtx(phase) {
							return {
								generation: this$1._generation,
								reinitCount: this$1._reinitCount,
								videoFlowId: this$1._videoFlowId,
								fname: this$1.fname,
								phase: phase,
								isTokenValid: null
							};
						}

						function reinitializeVideo(elem, myToken, reason) {
							var ctx = makeFlowCtx('reinit');
							if (this$1._reinitCount >= VIDEO_REINIT_MAX_ATTEMPTS) {
								logMedia(elem, 'video-reinit:abort', Object.assign({}, videoFlowExtra(ctx), {
									reason: reason,
									maxAttempts: VIDEO_REINIT_MAX_ATTEMPTS
								}));
								this$1.opt.play();
								return;
							}
							this$1._reinitCount += 1;
							this$1._generation += 1;
							var reinitGeneration = this$1._generation;
							ctx.generation = reinitGeneration;
							ctx.reinitCount = this$1._reinitCount;
							ctx.isTokenValid = function () {
								return reinitGeneration === this$1._generation;
							};

							logMedia(elem, 'video-reinit:start', Object.assign({}, videoFlowExtra(ctx), { reason: reason }));

							if (this$1._nativeMode) {
								elem._videoReinitInProgress = true;
								SPKManage.stopNativeVideo();
								if (reinitGeneration !== this$1._generation) {
									elem._videoReinitInProgress = false;
									return;
								}
								this$1._nativeAwaitingReinitPlay = true;
								syncNativeVideoBounds(elem);
								SPKManage.prepareNativeVideo(nativeVideoAbsolutePath(this$1.src), this$1.fname);
								logMedia(elem, 'video-reinit:end', Object.assign({}, videoFlowExtra(ctx), {
									native: true
								}));
								elem._videoReinitInProgress = false;
								return;
							}

							elem._videoReinitInProgress = true;
							elem.pause();
							elem.removeAttribute('src');
							elem.load();

							if (reinitGeneration !== this$1._generation) {
								elem._videoReinitInProgress = false;
								return;
							}
							elem.src = this$1.src;
							elem.load();
							logMedia(elem, 'video-reinit:end', Object.assign({}, videoFlowExtra(ctx), {}));
							elem._videoReinitInProgress = false;
						}

						function completeDirectPlayWithVisualGate(elem, myToken, isError4Recover) {
							var ctx = makeFlowCtx('preOptPlay');
							ctx.isTokenValid = function () {
								return myToken === this$1._directPlayToken;
							};

							prepareVideoPlayback(elem, 'preOptPlay', ctx, function (result) {
								if (myToken !== this$1._directPlayToken) {
									return;
								}
								if (result.pass) {
									if (isError4Recover) {
										elem._videoError4RecoverSkipSrvideoPlay = true;
									}
									this$1.opt.play();
									return;
								}
								if (result.reason === 'black-candidate' || result.blackCandidate) {
									reinitializeVideo(elem, myToken, result.reason || 'black-candidate');
									return;
								}
								logMedia(elem, 'pixel-sample:fallback-play', Object.assign({}, videoFlowExtra(ctx), {
									reason: result.reason || 'unknown'
								}));
								this$1.opt.play();
							});
						}

						function runDirectPlayInner(elem, myToken) {
							var ctx = makeFlowCtx('prePlay');
							ctx.isTokenValid = function () {
								return myToken === this$1._directPlayToken;
							};

							prepareVideoPlayback(elem, 'prePlay', ctx, function (timing) {
								if (myToken !== this$1._directPlayToken) {
									return;
								}

								logMedia(elem, 'loadeddata:direct-play:enter', Object.assign({}, videoFlowExtra(ctx), timing));

								elem.playsInline = true;
								elem.preload = 'auto';

								try {
									var p = elem.play();
									if (p && p.then) {
										p.then(function () {
											logMedia(elem, 'loadeddata:direct-play:resolved', Object.assign({}, videoFlowExtra(ctx), {
												fname: this$1.fname
											}));
											var isError4Recover = !!elem._videoError4RecoverPending;
											if (isError4Recover) {
												elem._videoError4RecoverPending = false;
											}
											completeDirectPlayWithVisualGate(elem, myToken, isError4Recover);
										}).catch(function (err) {
											logMedia(elem, 'loadeddata:direct-play:rejected', Object.assign({}, videoFlowExtra(ctx), {
												fname: this$1.fname,
												rejectName: err && err.name,
												rejectMessage: err && err.message
											}));
											console.warn('play failed, retry via reinit', err);
											reinitializeVideo(elem, myToken, 'play-rejected');
										});
									} else {
										completeDirectPlayWithVisualGate(elem, myToken, false);
									}
								} catch (e) {
									elem.pause();
									reinitializeVideo(elem, myToken, 'play-exception');
								}
							});
						}

						function runDirectPlay(elem, myToken) {
							runDirectPlayInner(elem, myToken);
						}

						function scheduleDirectPlayAfterLayoutReady(elem, myToken) {
							runDirectPlay(elem, myToken);
						}

						function startVideoSrcLoad(elem) {
							var ctx = makeFlowCtx('preSrc');
							ctx.isTokenValid = function () {
								return true;
							};
							prepareVideoPlayback(elem, 'preSrc', ctx, function (timing) {
								logMedia(elem, 'SRVideo:src:set-after-pipeline-ready', Object.assign({}, videoFlowExtra(ctx), timing));
								elem.src = this$1.src;
								elem.load();
							});
						}

						$video.each(function (idx, elem) {
							logMedia(elem, 'SRVideo:constructor', {
								fname: fname,
								src: src,
								videoIndex: idx,
								videoCount: $video.length,
								videoFlowId: this$1._videoFlowId,
								generation: this$1._generation,
								reinitCount: this$1._reinitCount
							});
						});

						set_video_event_handler($video, fname);
						if (!this$1._nativeMode) {
							set_video_retry_handler($video, fname, src);
						} else {
							set_native_video_retry_handler($video, fname, src);
							registerNativeVideoHandler(function (event, data) {
								this$1.$video.each(function (idx, elem) {
									handleNativeVideoEventForSRVideo(this$1, elem, event, data, makeFlowCtx);
								});
							});
						}

						if (!this$1._nativeMode) {
						$video
							.off('loadstart.direct-play loadeddata.direct-play canplaythrough')
							.on('loadstart', wrap_log(function (event) {
								var elem = event.target;
								this$1._directPlayToken += 1;
								// error 4 リトライの load() でも loadstart が走るため
								// _videoError4RecoverPending は direct-play:resolved で消費する
								if (!elem._videoError4RecoverPending) {
									elem._videoError4RecoverSkipSrvideoPlay = false;
								}
							}))
							.on('loadeddata', wrap_log(function (event) {
								var elem = event.target;
								var myToken = this$1._directPlayToken;
								scheduleDirectPlayAfterLayoutReady(elem, myToken);
							}));
						$video.each(function (idx, elem) {
							elem.preload = 'auto';
							elem.playsInline = true;
							elem._videoFlowMeta = {
								videoFlowId: this$1._videoFlowId,
								getGeneration: function () { return this$1._generation; },
								getReinitCount: function () { return this$1._reinitCount; }
							};
							startVideoSrcLoad(elem);
						});
						} else {
						$video.each(function (idx, elem) {
							elem.preload = 'auto';
							elem.playsInline = true;
							elem._videoFlowMeta = {
								videoFlowId: this$1._videoFlowId,
								getGeneration: function () { return this$1._generation; },
								getReinitCount: function () { return this$1._reinitCount; }
							};
							startNativeVideoLoadForSRVideo(this$1, elem, makeFlowCtx);
						});
						}

						$video.on('ended', wrap_log(function (event) {
							SRState.current_temp_rec_file_name = '';
							if (name_next_exam != 'EndExam') {
								//2018/01/12 Added By Abhijeet - #126  (Avoid Flickering while loading recording,pause or yesnobutton page ) - Start
								//#126 START Fetching next page data
								var element$2 = exam_pages[i + 1].element;
								var interview$2 = exam_pages[i + 1].interview;
								//#126 END Fetching next page data
								var yes_element_page_id = interview$1.yes_element_page_id;
								var no_element_page_id = interview$1.no_element_page_id;
								var nextRedirectPageInYesNoCase = '';
								var nextRedirectPageIdInYesNoCase = '';
								var name_next_exam2 = 'EndExam';
								//2018/01/08 Added by Kailas - #50 (On click of Yes/No button corresponing question will be loaded.) - Start

								if (element$2.max_replay_click_count != null) {
									SRState.replay_max_click_count = element$2.max_replay_click_count;
								}

								if (record_element_page_id != '') {
									var name_next_exam_yes = [];
									var name_next_exam_no = [];
									var name_next_exam_record_page = '';
									var isYesNoPage = false;

									if (record_element_page_id != '') {
										if (yes_element_page_id != '' && no_element_page_id != '') {

											name_next_exam_yes = yes_element_page_id.split(",");
											SPKManage.writeLog(`name_next_exam_yes array length: ` + name_next_exam_yes.length);
											var x;
											for (x = 0; x < name_next_exam_yes.length; x++) {
												if (element$1.element_page_id == name_next_exam_yes[x]) {
													isYesNoPage = true;
													//here checking current page element id with yes array values
													if (x == name_next_exam_yes.length - 1) {
														var interview_id$3 = interview$2.interview_id;
														SPKManage.setYesNoTypeAnswerJson(interview_id$3, "yes");
														nextRedirectPageIdInYesNoCase = record_element_page_id;
														nextRedirectPageInYesNoCase = `Exam-${interview$1.interview_id}-${record_element_page_id}`;
													} else {
														nextRedirectPageIdInYesNoCase = name_next_exam_yes[x + 1];
														nextRedirectPageInYesNoCase = `Exam-${interview$1.interview_id}-` + name_next_exam_yes[x + 1];
													}
												}// end of if current page id is in yes array	

												name_next_exam_yes[x] = `Exam-${interview$1.interview_id}-` + name_next_exam_yes[x];
												SPKManage.writeLog(`name_next_exam_yes: ` + name_next_exam_yes[x]);
											}
											name_next_exam_no = no_element_page_id.split(",");
											SPKManage.writeLog(`name_next_exam_no array length: ` + name_next_exam_no.length);
											for (x = 0; x < name_next_exam_no.length; x++) {
												if (element$1.element_page_id == name_next_exam_no[x]) {
													isYesNoPage = true;
													//here checking current page element id with no array values
													if (x == name_next_exam_no.length - 1) {
														var interview_id$3 = interview$2.interview_id;
														SPKManage.setYesNoTypeAnswerJson(interview_id$3, "no");
														nextRedirectPageIdInYesNoCase = record_element_page_id;
														nextRedirectPageInYesNoCase = `Exam-${interview$1.interview_id}-${record_element_page_id}`;
													} else {
														nextRedirectPageIdInYesNoCase = name_next_exam_no[x + 1];
														nextRedirectPageInYesNoCase = `Exam-${interview$1.interview_id}-` + name_next_exam_no[x + 1];
													}
												}// end of if current page id is in no array	

												name_next_exam_no[x] = `Exam-${interview$1.interview_id}-` + name_next_exam_no[x];
												SPKManage.writeLog(`name_next_exam_no: ` + name_next_exam_no[x]);
											}
										} // end of yes no page element not null if 
										//set record page name.
										name_next_exam_record_page = `Exam-${interview$1.interview_id}-${record_element_page_id}`;
										SPKManage.writeLog(`name_next_exam_record_page: ${name_next_exam_record_page}`);
									}//end of recordelementpageid check 



									SPKManage.writeLog(`SRState.curPageName ` + SRState.curPageName + ` name_next_exam_yes: ` + name_next_exam_yes + ` name_next_exam_no: ` + name_next_exam_no);
									//2018/01/10 Modified by Kailas - #164 ("Yes / No" type of the question may have multiple rows. So handle it accordingly.) - Start
									//check if currentPageName is a last page of yes or no question then next record page will be loaded.
									if (isYesNoPage) {
										//if it is yesno page
										name_next_exam2 = nextRedirectPageInYesNoCase;
										for (var nextPages = i + 1; nextPages < exam_pages.length; nextPages++) {
											element$2 = exam_pages[nextPages].element;
											interview$2 = exam_pages[nextPages].interview;
											var element_page_id$2 = element$2.element_page_id;
											if (element_page_id$2 == nextRedirectPageIdInYesNoCase) {
												if (nextPages + 1 < exam_pages.length) {
													var next = exam_pages[nextPages + 1];
													name_next_exam2 = `Exam-${next.interview.interview_id}-${next.element.element_page_id}`;
												} else {
													name_next_exam2 = 'EndExam';
												}
												break;
											}
										}

										SPKManage.writeLog(`#######element$2.type:` + element$2.type);
										if (element$2.type == 'pause' || element$2.type == 'record') {
											//If type pause or record then we load nextpage containt on same page
										}//End of element2 record or pause if
										else {
											mgr.showPage(name_next_exam);
										}

									} else {

										if (nextRedirectPageInYesNoCase == '' && nextRedirectPageIdInYesNoCase == '') {
											for (var nextPages = i + 1; nextPages < exam_pages.length; nextPages++) {
												element$2 = exam_pages[nextPages].element;
												interview$2 = exam_pages[nextPages].interview;
												var element_page_id$2 = element$2.element_page_id;
												if (element_page_id$2 == record_element_page_id) {
													if (nextPages + 1 < exam_pages.length) {
														var next = exam_pages[nextPages + 1];
														name_next_exam2 = `Exam-${next.interview.interview_id}-${next.element.element_page_id}`;
													} else {
														name_next_exam2 = 'EndExam';
													}
													break;
												}
											}
										}

										//#126 Code For yesno button page 
										element$2 = exam_pages[i + 1].element;
										interview$2 = exam_pages[i + 1].interview;

										if (element$2.yes_no_type == "1") {
											is_yes_no_page = 1;
											jquery$1('#yesnoDiv').show();
											jquery$1('#yesnoDiv').css('display', 'block');
											var $yes_button = jquery$1('#yes-button');
											var $no_button = jquery$1('#no-button');
											if ($yes_button != null) {
												$yes_button.on('click', wrap_log(function (event) {
													buttonYesNoClickFlag = true;
													SRState.is_yes_no_type_page = 1;
													timer.pause();
													terminated = true;
													SRState.reply_button_clicked = false;
													try {
														var temp_prev_page_visited = SRState.prev_page_visited - 1;
														SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + temp_prev_page_visited);
													} catch (e) { }
													SPKManage.writeLog(`yes button click. loading next page ${name_next_exam_yes}`);

													//var status_code = "S012"; //Added by Vijaya EIK-18 -> Yes button click status 
													//var interview_element_part_no = `${interview$2.interview_id}-${element$2.element_page_id}-${interview$2.part_no}`;

													//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, false);//added by Vijaya EIK-18

													mgr.showPage(name_next_exam_yes[0]);
												}));
											}
											if ($no_button != null) {
												$no_button.on('click', wrap_log(function (event) {
													buttonYesNoClickFlag = true;
													SRState.is_yes_no_type_page = 1;
													timer.pause();
													terminated = true;
													SRState.reply_button_clicked = false;
													try {
														var temp_prev_page_visited = SRState.prev_page_visited - 1;
														SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + temp_prev_page_visited);
													} catch (e) { }
													SPKManage.writeLog(`no button click. loading next page ${name_next_exam_no}`);

													//var status_code = "S013"; //Added by Vijaya EIK-18 -> no button click status 
													//var interview_element_part_no = `${interview$2.interview_id}-${element$2.element_page_id}-${interview$2.part_no}`;

													//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, false);//added by Vijaya EIK-18	

													mgr.showPage(name_next_exam_no[0]);
												}));
											}
										}
									}
								} else {
									//#126 Other than YesNoType questions
									var audio_filename1 = element$2.audio_filename;
									var interview_id$2 = interview$2.interview_id;
									var element_page_id$2 = element$2.element_page_id;

									if (i + 2 < exam_pages.length) {
										var next = exam_pages[i + 2];
										name_next_exam2 = `Exam-${next.interview.interview_id}-${next.element.element_page_id}`;
									} else {
										name_next_exam2 = 'EndExam';
									}

									if (element$2.type == 'pause' || element$2.type == 'record') {
										//If type pause or record then we load nextpage containt on same page
									}//End of element2 record or pause if
									else {
										mgr.showPage(name_next_exam);
									}

								}//#126 else End Other than YesNoType questions

								//Check is next page available after yesno type button
								var showNextButtonPage2 = false;
								if ((element$2.type == 'record') && (name_next_exam2 != 'EndExam')) {
									showNextButtonPage2 = true;
								}

								//#126 Check recording needed on nextPage 
								var needs_recording = (element$2.type == 'record')
									&& interview$2.is_recording
									&& interview$2.record_file_name;
								var auto_transition = !(element$2.type == 'playback' && element$2.duration == 0);

								//#126 set timer after video end for recording or pause page 
								var timer;
								if (needs_recording) {
									timer = new RecordTimer(element$2.duration);
									timer1 = new RecordTimer(element$2.duration);
								} else {
									timer = new CountDownTimer(element$2.duration);
									timer1 = new CountDownTimer(element$2.duration);
								}

								try {
									if (booklet_manager != null && interview$2 != null && element$2 != null) {
										var breadcrumb = booklet_manager.getBreadcrumbInfo(interview$2.interview_id, element$2.element_page_id);
										if (breadcrumb != null) {
											var task_name = breadcrumb.task_name;
											if (breadcrumb.task_name != null) {
												jquery$1('#examHeader').text(breadcrumb.task_name);
											}
										}
									}
								} catch (e) {

								}


								//#126 START Change breadcrumb after loading nextpage containt
								/*var breadcrumb = booklet_manager.getBreadcrumbInfo(interview$2.interview_id, element$2.element_page_id);
								var task_name = breadcrumb.task_name;
								if (breadcrumb.task_max) {
									if (breadcrumb.task_now) {
										task_name += ' ' + breadcrumb.task_now;
									}
								}
		
								var task_states = [];
								var state_max = breadcrumb.state_max;
								var state_now = breadcrumb.state_now;
								var state_str = breadcrumb.state_str;
								if (state_max && state_str.length >= state_max) {
									for (var i$1 = 1; i$1 <= state_max; ++i$1) {
										task_states.push({
											number: i$1,
											str: state_str[i$1-1],
											current: (i$1 == state_now) ? 'class="current"' : '',
										});
									}
								}	
								
								var breadcrumpData={'task_name': task_name,
										'task_max': state_max,
										'task_states': task_states,
										'num_states': task_states.length};
								
								var breadcrumpTags=' <ul id="menuList" class="section_number num{{num_states}}">'
														+'{{#task_states}}'
														+'<li {{{current}}}>'
														+'	<div class="number">{{number}}</div>'
														+'	<div class="text"><span>{{{str}}}</span></div>'
														+'</li>'
														+'{{/task_states}}'
														+'</ul>';
								
								var htmlTemp = mustache.to_html(breadcrumpTags, breadcrumpData);
								jquery$1('#menuList').html(htmlTemp);		
								 
								jquery$1('#menuList').hide();
								jquery$1('#menuList').show();*/
								//#126 END Change breadcrumb after loading nextpage containt


								var $now_recording = jquery$1('#js-now-recording');
								var resume_element_id = "";

								var recordingThresholdMax = SPKManage.getRecordingThresholdMax();
								var recordingThresholdMin = SPKManage.getRecordingThresholdMin();
								var showRecordingVolumeLevelError = true; //added by Vijaya EIK-290
								var popUpDisplayed = false; //added by Vijaya EIK-290
								//2018/11/28 Added by Sharad - #CBTIBA-84 (Set Next Button Display Timing Question Page Wise.) - Start
								var recordingThresholdCheckingDuration = SPKManage.getRecordingThresholdCheckingDuration(); // in milliseconds
								if (element$2.hasOwnProperty('next_button_display_timing')) {
									SPKManage.writeLog(`next_button_display_timing present in element$2: ${interview_id$1}-${element_page_id$1} `);
									if (element$2.next_button_display_timing != null && element$2.next_button_display_timing != "") {
										recordingThresholdCheckingDuration = element$2.next_button_display_timing;
									}
								}

								resume_element_id = element_page_id$1;

								//2018/11/28 Added by Sharad - #CBTIBA-84 (Set Next Button Display Timing Question Page Wise.) - End
								var isCheckRecordingThreshold = false;
								/*var isMicLevelNotZero = false;*/
								var isRecordLevelBelowRecordingThreshold = false;
								var recordLevelBelowRecordingThresholdStartMilliSecond = 0;
								var isMessageShown = false;

								//2018/04/25 Abdded by Abhijeet - #274 
								var startedRecordingTimeInMillisecond = 0;
								var isNeedToSetStartedRecordingTimeInMillisecond = true;
								//2018/04/25 Abdded by Abhijeet - #274 


								//on load of screen hide click_next_button message.
								var $click_next_message = jquery$1('#click_next_message');
								$click_next_message.hide();
								var $next_button_control_with_msg = jquery$1('#next-button');
								$next_button_control_with_msg.hide();
								SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> $click_next_message.hide();");
								var chk_mic_mute_occurence = 0;
								var micMuteCheckFlag = SPKManage.getMicMuteCheckFlag();
								//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- START
								var sysDate = new Date();
								var sysTime = sysDate.getTime();
								var recStartSysTime = sysTime;
								var recStartDuration = -1;
								var checklastMilliSecondCount = 0;
								var extraSecondsFlag = 1;
								var extraSecondsFlagDuration = 1;
								//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- END
								timer1.onTick(wrap_log(function () {
									var sec = timer1.getSecond();

									if (element$2.duration && timer1.isStarted()) {
										var totalDuration = parseInt(element$2.duration);
										var timerText = timer1.text();
										//Added By Sharad CBT#152 Pause timer on 00:00 for one seconds   -- start
										if (extraSecondsFlag == 1) {
											extraSecondsFlagDuration = timerText;
										}
										else {
											var wait_flag = SPKManage.deleyForTimerStop();
											if (wait_flag) {
												timer1.stop();
											}
											timerText = extraSecondsFlagDuration;
										}
										//Added By Sharad CBT#152 Pause timer on 00:00 for one seconds   -- End
										checkType = "pause";
										SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> timerText: " + timerText);
										//Added By Sharad CBTIBA#86 Set Priority To Reply Button Click Problem  -- START
										/*if(timerText=="00 : 01")
										{
											checklastMilliSecondCount=checklastMilliSecondCount+1;
											SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> checklastMilliSecondCount: "+checklastMilliSecondCount);
											if(checklastMilliSecondCount==6)
											{	
												var $replay_button = jquery$1('#replayButton');	
												$replay_button.hide();
											}
										}*/
										//Added By Sharad CBTIBA#86 Set Priority To Reply Button Click Problem -- END
										if (element$2.type == 'record' && extraSecondsFlag == 1) {
											checkType = "record";
											var milliSecondsToDisableNextBUtton = 1000;
											var secondsToSkipCheckRecordingThreshold = 1;
											SPKManage.setEvnRecordCheckPage(false);
											var recordLevel = SPKManage.getRecordLevel(SRState.rec_volume);

											/*if(micMuteCheckFlag && SPKManage.isMicMute(SRState.rec_volume)){*/
											if (micMuteCheckFlag && SPKManage.isMicMuteCheckUsingExe()) { //Changed by Vijaya EIK-341
												chk_mic_mute_occurence++;
											} else {
												chk_mic_mute_occurence = 0
											}
											SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> recordingThresholdMax: " + recordingThresholdMax + "recordingThresholdMin: " + recordingThresholdMin + " recordingThresholdCheckingDuration: " + recordingThresholdCheckingDuration + " recordLevel: " + recordLevel);

											var timerStr = timerText.split(":");
											var timerMinutes = timerStr[0];
											var timerSeconds = timerStr[1];


											// Convert the duration in milliseconds
											var durationInMilliSeconds = ((parseInt(timerMinutes) * 60) + parseInt(timerSeconds)) * 1000;
											//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- START
											var sysDateInRec = new Date();
											var sysTimeInRec = sysDateInRec.getTime();
											//SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> sysTimeInRec "+sysTimeInRec);
											//SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> recStartSysTime "+recStartSysTime);
											//SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> recStartSysTime-sysTimeInRec "+((sysTimeInRec - recStartSysTime)/1000));

											if (((sysTimeInRec - recStartSysTime) / 1000) >= 6) {
												SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> recStartDuration[" + recStartDuration + "] durationInMilliSeconds[" + durationInMilliSeconds + "]");
												if (recStartDuration == durationInMilliSeconds) {
													SPKManage.writeLog("Error In Audio Recording");
													throw new Error('ErrorInRecProgress');
												} else {
													recStartSysTime = sysTimeInRec;
													recStartDuration = durationInMilliSeconds;
												}
											}
											//Added By Abhijeet CBT#408 Show Error Popup if audio recording problem  -- END

											//2018/04/25 Abdded by Abhijeet - #274  -- START
											if (isNeedToSetStartedRecordingTimeInMillisecond) {
												startedRecordingTimeInMillisecond = durationInMilliSeconds;
												isNeedToSetStartedRecordingTimeInMillisecond = false;
											}


											if (!SPKManage.checkMicAvalableExamPage()) {
												//SPKManage.stopRecordSound(SRState.rec_volume);
												//SPKManage.stopMicCheck();
												SRState.current_temp_rec_file_name = interview$2.record_file_name.replace('%login_id%', SRState.login_id);
												throw new Error('マイクが接続されていません');
											} else {
												SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> startedRecordingTimeInMillisecond : " + startedRecordingTimeInMillisecond);
												var totalRecordingTimeInMillisecond = startedRecordingTimeInMillisecond - durationInMilliSeconds;
												SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> totalRecordingTimeInMillisecond : " + totalRecordingTimeInMillisecond);

												if (startedRecordingTimeInMillisecond != totalRecordingTimeInMillisecond && totalRecordingTimeInMillisecond > 2000 && chk_mic_mute_occurence >= 1 && micMuteCheckFlag) { //Changed occurence 10 to #EIK-184 Mic mute 20191220	
													SPKManage.stopRecordSound(100);
													SPKManage.stopMicCheck();
													SRState.current_temp_rec_file_name = interview$2.record_file_name.replace('%login_id%', SRState.login_id);
													/* Commented code by Mayuri 20191012 mic mute change not working for new headset on clint m/c
													// Added for Mic mute changes #EIK-184
													SPKManage.isMuteSet();
													// Added for Mic mute changes #EIK-184
													*/
													throw new Error('マイクがミュートになっています。');    //Added by Sharad for EIK#284
												}
											}
											//2018/04/25 Abdded by Abhijeet - #274  -- END


											//2018/01/08 Modified by Kailas - #53 (disable next button 2 seconds before timer finish.) & #54 (display next button click message) - Start
											//check recording threshold  & next botton disable code will call only if next page available.
											if (name_next_exam != 'EndExam') {
												//check recording threshold only if user speaks, otherwise no message will be displayed.
												/* Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- START
												if(isCheckRecordingThreshold == false && (recordLevel > recordingThresholdMax)){
													isCheckRecordingThreshold = true;
													SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> isCheckRecordingThreshold: "+isCheckRecordingThreshold);
												}
												Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- END
												*/

												/*if((isMicLevelNotZero == false) && (recordLevel > 0)){
													isMicLevelNotZero = true;
													SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> isMicLevelNotZero: "+isMicLevelNotZero);
												}*/
												/* Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- START
												if(isCheckRecordingThreshold == true){
													//show message only if recordLevel <= recordingThreshold.
													if(recordLevel <= recordingThresholdMin){
													 Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- END
													*/
												SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> if(recordLevel <= recordingThresholdMin){ ");
												//set below recording threshold start time.
												/*
												Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- START
												if(recordLevelBelowRecordingThresholdStartMilliSecond == 0){
													recordLevelBelowRecordingThresholdStartMilliSecond = durationInMilliSeconds;
													SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> if(recordLevelBelowRecordingThresholdStartMilliSecond == 0){ recordLevelBelowRecordingThresholdStartMilliSecond: "+recordLevelBelowRecordingThresholdStartMilliSecond);
												}else
													Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- END
												*/
												if (recordLevelBelowRecordingThresholdStartMilliSecond == 0) {
													recordLevelBelowRecordingThresholdStartMilliSecond = durationInMilliSeconds;
												}
												SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> recordLevelBelowRecordingThresholdStartMilliSecond" + recordLevelBelowRecordingThresholdStartMilliSecond + " ::durationInMilliSeconds:" + durationInMilliSeconds);
												if (showNextButtonPage2 && (isMessageShown == false) && (recordLevelBelowRecordingThresholdStartMilliSecond - durationInMilliSeconds >= recordingThresholdCheckingDuration) && recordingThresholdCheckingDuration != "-1") { // conditon for which message will be shown.
													SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> (durationInMilliSeconds) >= recordingThresholdCheckingDuration) && recordingThresholdCheckingDuration!=-1");
													var $click_next_message = jquery$1('#click_next_message');
													$click_next_message.show();
													var $next_button_control_with_msg = jquery$1('#next-button');
													$next_button_control_with_msg.show();
													SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> $click_next_message.show();");
													isMessageShown = true;
												}

												//added by vijaya EIK-290 start												
												if (recordLevel > recordingThresholdMax) {
													SPKManage.writeLog("In if recordLevel: " + recordLevel + "recordingThresholdMax : " + recordingThresholdMax);
													SPKManage.writeLog("before showRecordingVolumeLevelError set: " + showRecordingVolumeLevelError);
													showRecordingVolumeLevelError = false;
													SPKManage.writeLog("after showRecordingVolumeLevelError set: " + showRecordingVolumeLevelError);
												}
												//added by vijaya EIK-290 end

												/* Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- START
											}else{
												//reset below recording threshold start time to zero.
												recordLevelBelowRecordingThresholdStartMilliSecond = 0;
												SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> else recordLevelBelowLevelStartSecond: "+recordLevelBelowRecordingThresholdStartMilliSecond);
											}
											 Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- END
											
										}
										 */
												SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> durationInMilliSeconds: " + durationInMilliSeconds);
												if (durationInMilliSeconds == milliSecondsToDisableNextBUtton) {
													var $next_button = jquery$1('#next-button');
													if ($next_button != null) {
														SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> next-button disabled.");
														//$next_button.attr("disabled","disabled");
														$next_button.off('click');
													}
													var $replay_button1 = jquery$1('#replayButton');
													if ($replay_button1 != null) {
														//$replay_button1.hide();
													}
												}
											}
											//2018/01/08 Modified by Kailas - #53 (disable next button 2 seconds before timer finish.) & #54 (display next button click message) - End
										}
										jquery$1('#js-countdown').text(timerText);
									}

									if (SPKManage.getRecordingProgress() > 0.0) {
										// リプレイボタン表示制御（録音開始後のみ）
										var maxReplayClickCnt = 0;
										if (element$2.max_replay_click_count != null) {
											maxReplayClickCnt = element$2.max_replay_click_count;
										}
										updateReplayButtonVisibility(maxReplayClickCnt);
										$now_recording.show();
									} else {
										$now_recording.hide();
									}
									//Added By Sharad CBT#152 Pause timer on 00:00 for one seconds   -- start
									SRState.mouse_click_flag = "1";
									var $mouseClickEvent = jquery$1('#mouseClickEvent');
									if ($mouseClickEvent != null) {
										$mouseClickEvent.on('click', wrap_log(function (event) {
											var xaxis = event.pageX;
											var yaxis = event.pageY;
											if (SRState.mouse_click_flag == "1") {
												SRState.mouse_click_flag = "2";
												//Conditional target element check Added by Atish
												if (event.target.id != "" && event.target.className != "") {
													SPKManage.writeLog("> X-axis: " + xaxis + " > Y-axis: " + yaxis + " (id = " + event.target.id + ", " + "className = " + event.target.className + ", " + "tagName = " + event.target.tagName + ")");
												}
												else if (event.target.id != "") {
													SPKManage.writeLog("> X-axis: " + xaxis + " > Y-axis: " + yaxis + " (id = " + event.target.id + ", " + "tagName = " + event.target.tagName + ")");
												}
												else if (event.target.className != "") {
													SPKManage.writeLog("> X-axis: " + xaxis + " > Y-axis: " + yaxis + " (className = " + event.target.className + ", " + "tagName = " + event.target.tagName + ")");
												}
												else {
													SPKManage.writeLog("> X-axis: " + xaxis + " > Y-axis: " + yaxis + " (tagName = " + event.target.tagName + ")");
												}
											}
										}));
									}
									if (sec == 0) {
										SPKManage.writeLog("extraSecondsFlag:  " + extraSecondsFlag);
										if (extraSecondsFlag == 1) {
											sec = 1;
											extraSecondsFlag++;
											SPKManage.stopMicCheck();
											SPKManage.stopRecordSound(100);
											$now_recording.hide();
											$now_recording.hide();
											var $replay_button1 = jquery$1('#replayButton');
											var $next_button = jquery$1('#next-button');
											var $yes_button = jquery$1('#yes-button');
											var $no_button = jquery$1('#no-button');
											var $click_next_message = jquery$1('#click_next_message');
											$click_next_message.hide();
											if ($replay_button1 != null) {
												$replay_button1.hide();
											}
											if ($next_button != null) {
												$next_button.hide();
											}
											if ($yes_button != null || $no_button != null) {
												$yes_button.hide();
												$no_button.hide();

												//Added by Atish to add YES No button visibilty log for only yes no type questions
												if (element$2.yes_no_type == "1") {
													SPKManage.writeLog('YES NO button hidden.'); 		// Added by Atish to check YES NO button visibility log   --- #Eik-2593
												}

											}
										}
									}
									//Added By Sharad CBT#152 Pause timer on 00:00 for one seconds   -- End   
									return (sec != 0)
								}));

								if (element$2.alarm_sound) {
									jquery$1('#js-start-beep').on('ended', wrap_log(function (event) {
										timer1.start();
										jquery$1('#js-countdown-timer').css('visibility', 'visible');
									})).get(0).play();

								} else {
									jquery$1('#js-countdown-timer').show();
									timer1.start();
									jquery$1('#js-countdown-timer').css('visibility', 'visible');
								}
								timer1.onStop(wrap_log(function () {
									//2018/01/25 Added by Kailas - #182 (show error page if recording threshold not crossed on recording page)  - Start
									/*if((isMicLevelNotZero == false) && (element$2.type == 'record')){
										throw new Error('Volume is low Or Check the mic setting.');
									}*/
									//2018/01/25 Added by Kailas - #182 (show error page if recording threshold not crossed on recording page)  - End

									jquery$1('#volumeLevelErrorOk').on('click', function () {
										SPKManage.writeLog("VLMErrorPopup : Clicked on Next button on volumeLevelError");
										hideSpkBodyModal('volError', true);
										jquery$1('#volumeLevelErrorOk').css("display", "none");
										jquery$1('#volumeLevelErrorOk').off('click');
										SPKManage.writeLog("VLMErrorPopup:NextButtton double click off ");
										SPKManage.writeLog(`convert mp3 process result: ${resume_element_id}`);
										if (SRState.is_yes_no_type_page == 1) {
											resume_element_id = "";
											SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
										}
										else {
											SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
										}
										/*Added By Sharad For CBT#135 End */
										setTimeout(wrap_log(function () {
											if (SPKManage.saveRecordedSound(wavPath)) {
												SPKManage.writeLog(`convertMP3 timer1.onStop 11 mp3Path.replace('.mp3','##.mp3')` + mp3Path.replace('.mp3', '##.mp3'));
												SPKManage.convertMP3(wavPath, mp3Path.replace('.mp3', '##.mp3'), SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, interview_id$1, element_page_id$1);
											} else {
												SPKManage.writeLog(`convert mp3 process result: saveRecordedSound failed 001`);
												throw new Error('音声の保存に失敗しました');
											}

											var conv_proc = function () {
												if (!SPKManage.isConvertingMP3()) {
													ended_conv_mp3 = true;
													return
												}
												setTimeout(wrap_log(conv_proc), 100);
											};
											conv_proc();
										}), 10);

										var wait_proc = function () {
											if (ended_conv_mp3 && ended_alarm) {
												var exit_value = SPKManage.getConvMP3Result();
												if (exit_value != 0) {
													SPKManage.writeLog(`convert mp3 process result: ${exit_value}`);
													throw new Error('音声の保存に失敗しました')
												}

												if (SPKManage.isMeargeException()) {
													SPKManage.writeLog(`convert mp3 process result: ${exit_value}`);
													throw new Error('音声の保存に失敗しました')
												}

												//checkMP3FileSize(mp3Path);
												needs_set_resume = true;
												try {
													var temp_prev_page_visited = SRState.prev_page_visited - 1;
													SPKManage.writeLog(`Replay click file name:` + interview$1.record_file_name + ` count:` + temp_prev_page_visited);
													if (temp_prev_page_visited > 0) {
														SPKManage.setReplyClickCount(interview_id$1, temp_prev_page_visited, SRState.login_id);
													}
												} catch (e) { }

												if (SPKManage != null && SPKManage.isAudioError(SRState.login_id) != null && SRState.popup_show_flag == 0) {
													var questionNum = SPKManage.isAudioError(SRState.login_id);

													var interview_element_part_no;

													var status_code = "S017"; //Added by Vijaya EIK-18 -> Info level error occured status
													if (resume_element_id != "") {
														interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
													} else {
														interview_element_part_no = `${interview$1.interview_id}-NA-${interview$1.part_no}`;
													}

													SPKManage.setInfoLevelErrorFlag(true);
													//var interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
													SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, true);//added by Vijaya EIK-18


													var showInfoLevelErrPopUp = setInterval(function () {
														showSpkElementModal('infoLevelError');
														document.getElementById("questionMsg").innerHTML = "Audio recording info level error occures at " + questionNum + ".<br>Please call the administrator.";
														clearInterval(showInfoLevelErrPopUp);
													}, 3000);

													Mousetrap.bind('ctrl+alt+c', function (e) {
														var myVar = setInterval(function () {
															SPKManage.setCtrlAltCFlag(true);
															SPKManage.sendStatusInfoErrorButContinue(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no);
															hideSpkElementModal('infoLevelError', true);
															document.getElementById("questionMsg").innerHTML = '';
															mgr.showPage(name_next_exam2);
															clearInterval(myVar);
															return false;
														}, 3000);
													});

													SRState.popup_show_flag = SRState.popup_show_flag + 1;

												} else {
													mgr.showPage(name_next_exam2);
													return false;
												}

												return
											}
											setTimeout(wrap_log(wait_proc), 100);
										};
										wait_proc();

									}); //END Of VLM Error Next Button Click

									enable_pause = false;
									if (needs_recording) {
										var ended_alarm = false;
										var ended_conv_mp3 = false;

										if (element$2.alarm_sound) {
											jquery$1('#js-end-beep').get(0).play();
											jquery$1('#js-end-beep').on('ended', wrap_log(function (event) {
												ended_alarm = true;
											}));
										} else {
											ended_alarm = true;
										}

										var ansDir = SPKManage.makeAnsBaseDir();
										var fname = interview$2.record_file_name.replace('%login_id%', SRState.login_id);
										var wavPath = `cbt/testResults/spkexam/wav/${fname}_${SRState.prev_page_visited}.wav`;
										var mp3Path = `${ansDir}${fname}`;
										/*document.getElementById("infoLevelError").style.visibility='visible';
										  document.getElementById("infoLevelError").style.opacity = 1;*/
										/*Added By Sharad For CBT#135 Start */


										//added By Vijaya EIK-290 start
										if (showRecordingVolumeLevelError) {
											SPKManage.writeLog("Recorded answer level is continous lower than expected:Warning Popup shows to user on RecordTimerEnd");
											showSpkBodyModal('volError');
										} else {
											//added By Vijaya EIK-290 end

											SPKManage.writeLog(`convert mp3 process result: ${resume_element_id}`);
											if (SRState.is_yes_no_type_page == 1) {
												resume_element_id = "";
												SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
											}
											else {
												SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
											}
											/*Added By Sharad For CBT#135 End */
											setTimeout(wrap_log(function () {
												if (SPKManage.saveRecordedSound(wavPath)) {
													SPKManage.writeLog(`convertMP3 timer1.onStop 11 mp3Path.replace('.mp3','##.mp3')` + mp3Path.replace('.mp3', '##.mp3'));
													SPKManage.convertMP3(wavPath, mp3Path.replace('.mp3', '##.mp3'), SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, interview_id$1, element_page_id$1);
												} else {
													SPKManage.writeLog(`convert mp3 process result: saveRecordedSound failed 002`);
													throw new Error('音声の保存に失敗しました');
												}

												var conv_proc = function () {
													if (!SPKManage.isConvertingMP3()) {
														ended_conv_mp3 = true;
														return
													}
													setTimeout(wrap_log(conv_proc), 100);
												};
												conv_proc();
											}), 10);

											var wait_proc = function () {
												if (ended_conv_mp3 && ended_alarm) {
													var exit_value = SPKManage.getConvMP3Result();
													if (exit_value != 0) {
														SPKManage.writeLog(`convert mp3 process result: ${exit_value}`);
														throw new Error('音声の保存に失敗しました')
													}

													if (SPKManage.isMeargeException()) {
														SPKManage.writeLog(`convert mp3 process result: ${exit_value}`);
														throw new Error('音声の保存に失敗しました')
													}

													//checkMP3FileSize(mp3Path);
													needs_set_resume = true;
													try {
														var temp_prev_page_visited = SRState.prev_page_visited - 1;
														SPKManage.writeLog(`Replay click file name:` + interview$1.record_file_name + ` count:` + temp_prev_page_visited);
														if (temp_prev_page_visited > 0) {
															SPKManage.setReplyClickCount(interview_id$1, temp_prev_page_visited, SRState.login_id);
														}
													} catch (e) { }

													if (SPKManage != null && SPKManage.isAudioError(SRState.login_id) != null && SRState.popup_show_flag == 0) {
														var questionNum = SPKManage.isAudioError(SRState.login_id);

														var interview_element_part_no;

														var status_code = "S017"; //Added by Vijaya EIK-18 -> Info level error occured status
														if (resume_element_id != "") {
															interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
														} else {
															interview_element_part_no = `${interview$1.interview_id}-NA-${interview$1.part_no}`;
														}

														SPKManage.setInfoLevelErrorFlag(true);
														//var interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
														SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, true);//added by Vijaya EIK-18


														var showInfoLevelErrPopUp = setInterval(function () {
															showSpkElementModal('infoLevelError');
															document.getElementById("questionMsg").innerHTML = "Audio recording info level error occures at " + questionNum + ".<br>Please call the administrator.";
															clearInterval(showInfoLevelErrPopUp);
														}, 3000);

														Mousetrap.bind('ctrl+alt+c', function (e) {
															var myVar = setInterval(function () {
																SPKManage.setCtrlAltCFlag(true);
																SPKManage.sendStatusInfoErrorButContinue(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no);

																//SPKManage.setCtrlAltCFlag(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji,true);
																hideSpkElementModal('infoLevelError', true);
																document.getElementById("questionMsg").innerHTML = '';
																mgr.showPage(name_next_exam2);
																clearInterval(myVar);
															}, 3000);
														});

														SRState.popup_show_flag = SRState.popup_show_flag + 1;

													} else {
														mgr.showPage(name_next_exam2);
													}

													return
												}
												setTimeout(wrap_log(wait_proc), 100);
											};
											wait_proc();
										} // VLM Error check condition End of else	 
									} else {
										if (element$2.alarm_sound) {
											jquery$1('#js-end-beep').get(0).play();
											jquery$1('#js-end-beep').on('ended', wrap_log(function (event) {
												if (auto_transition) {
													if (element$2.type == 'record') {
														needs_set_resume = true;
													}

													if (buttonYesNoClickFlag == false) {
														//SPKManage.writeLog(`Yes / No button is not selected. Calling showPage() from onStop`);
														SRState.yes_no_page_map[SRState.curPageName] = SRState.yes_no_page_map[SRState.curPageName] == null ? 1 : SRState.yes_no_page_map[SRState.curPageName] + 1;
														if (element$2.yes_no_type == "1" && (SRState.yes_no_page_map[SRState.curPageName] == null || SRState.yes_no_page_map[SRState.curPageName] <= 1)) {
															//SRState.prev_page_visited = 1;
															//SRState.prev_page_visited = 0;
															is_yes_no_page = 1;
															prev_page_visited_temp = SRState.prev_page_visited - 1;
															if (SRState.replay_max_click_count == prev_page_visited_temp && SRState.replay_max_click_count != 0) {
																SRState.reply_button_clicked = false;
																SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
																mgr.showPage(name_next_exam2);
															}
															else {
																if (SRState.reply_button_clicked == true) {
																	SRState.reply_button_clicked = false;
																	SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
																	SPKManage.writeLog(`Replay click & timer end thats why moving to next Question`);
																	mgr.showPage(name_next_exam2);
																}
																else {
																	yesNoReplyButtonCheckFlag = true;
																	SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
																	pageManager.reloadPreviousPage(SRState.curPageName);
																}
															}
														} else {
															SRState.yes_no_page_map[SRState.curPageName] = 0;
															SRState.reply_button_clicked = false;
															mgr.showPage(name_next_exam2);
														}
														//mgr.showPage(name_next_exam2);
													} else {
														SPKManage.writeLog(`Yes / No button is selected. Skipping showPage() from onStop`);
													}
												}
											}));
										} else {
											if (auto_transition) {
												if (element$2.type == 'record') {
													needs_set_resume = true;
												}

												if (buttonYesNoClickFlag == false) {
													//SPKManage.writeLog(`Yes / No button is not selected. Calling showPage() from onStop`);
													SRState.yes_no_page_map[SRState.curPageName] = SRState.yes_no_page_map[SRState.curPageName] == null ? 1 : SRState.yes_no_page_map[SRState.curPageName] + 1;
													if (element$2.yes_no_type == "1" && (SRState.yes_no_page_map[SRState.curPageName] == null || SRState.yes_no_page_map[SRState.curPageName] <= 1)) {
														//SRState.prev_page_visited = 1;
														//SRState.prev_page_visited = 0;
														is_yes_no_page = 1;
														prev_page_visited_temp = SRState.prev_page_visited - 1;
														if (SRState.replay_max_click_count == prev_page_visited_temp && SRState.replay_max_click_count != 0) {
															SRState.reply_button_clicked = false;
															SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
															mgr.showPage(name_next_exam2);
														}
														else {
															// SPKManage.writeLog(`SRState.reply_button_clicked:::`+SRState.reply_button_clicked);
															if (SRState.reply_button_clicked == true) {
																SRState.reply_button_clicked = false;
																SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
																SPKManage.writeLog(`Replay click & timer end thats why moving to next Question`);
																mgr.showPage(name_next_exam2);
															}
															else {
																yesNoReplyButtonCheckFlag = true;
																SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
																pageManager.reloadPreviousPage(SRState.curPageName);
															}
														}
													} else {
														SRState.yes_no_page_map[SRState.curPageName] = 0;
														SRState.reply_button_clicked = false;
														mgr.showPage(name_next_exam2);
													}
													//mgr.showPage(name_next_exam2);
												} else {
													SPKManage.writeLog(`Yes / No button is selected. Skipping showPage() from onStop`);
												}

											}
										}
									}

								}));



								if (element$2.type == 'pause' && audio_filename1) {
									bindNativeVideoPlayback($video, audio_filename1, data_dir + `spk/${booklet_name}/${interview_id$1}/${audio_filename1}`);
								}

								if (element$2.type == 'record' && audio_filename1) {
									bindNativeVideoPlayback($video, audio_filename1, data_dir + `spk/${booklet_name}/${interview_id$1}/${audio_filename1}`);
								}


								var ansDirNext = SPKManage.makeAnsBaseDir();
								var fnameNext = (interview$2.record_file_name.replace('%login_id%', SRState.login_id)).replace('.mp3', '##.mp3');
								var wavPathNext = `cbt/testResults/spkexam/wav/${fnameNext}_${SRState.prev_page_visited}.wav`;
								var mp3PathNext = `${ansDirNext}${fnameNext}`;



								if (element$2.type == 'record' && showNextButtonPage2) {
									var $next_button = jquery$1('#next-button');
									// $next_button.show();
									$next_button.attr("display", "block");
									/* SRState.VLMError_popup_display=false;*/
									if ($next_button != null) {
										$next_button.on('click', wrap_log(function (event) {
											jquery$1('#volumeLevelErrorOk').on('click', wrap_log(function () {
												SPKManage.writeLog("VLMErrorPopup : Clicked on Next button on volumeLevelError");
												hideSpkBodyModal('volError', true);
												jquery$1('#volumeLevelErrorOk').css("display", "none");
												jquery$1('#volumeLevelErrorOk').off('click');
												SPKManage.writeLog("VLMErrorPopup:NextButtton double click off ");
												if (!SPKManage.saveRecordedSound(wavPathNext)) {
													SPKManage.writeLog(`convert mp3 process result: saveRecordedSound failed 003`);
													throw new Error('音声の保存に失敗しました');
												}

												/*Added By Sharad For CBT#135 Start */
												if (SRState.is_yes_no_type_page == 1) {
													resume_element_id = "";
													SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
												}
												else {
													SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
												}

												/*Added By Sharad For CBT#135 End */
												SPKManage.writeLog(`convertMP3 $next_button 22 mp3PathNext` + mp3PathNext);
												SPKManage.convertMP3(wavPathNext, mp3PathNext, SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, interview_id$1, element_page_id$1);
												SPKManage.writeLog(`next button click. loading next page ${name_next_exam}`);
												if (!SPKManage.isMeargeException()) {
													try {
														var temp_prev_page_visited = SRState.prev_page_visited - 1;
														SPKManage.writeLog(`Replay click file name:` + interview$1.record_file_name + ` count:` + temp_prev_page_visited);
														if (temp_prev_page_visited > 0) {
															SPKManage.setReplyClickCount(interview_id$1, temp_prev_page_visited, SRState.login_id);
														}
													} catch (e) { }
													//var popupShowFlag = false;
													if (SPKManage != null && SPKManage.isAudioError(SRState.login_id) != null && SRState.popup_show_flag == 0) {

														var interview_element_part_no;

														var status_code = "S017"; //Added by Vijaya EIK-18 -> Info level error occured status
														if (resume_element_id != "") {
															interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
														} else {
															interview_element_part_no = `${interview$1.interview_id}-NA-${interview$1.part_no}`;
														}
														SPKManage.setInfoLevelErrorFlag(true);
														//var interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
														SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, true);//added by Vijaya EIK-18

														var showInfoLevelErrPopUp = setInterval(function () {
															var questionNum = SPKManage.isAudioError(SRState.login_id);

															showSpkElementModal('infoLevelError');
															document.getElementById("questionMsg").innerHTML = "Audio recording info level error occures at " + questionNum + "<br>Please call the administrator.";
															clearInterval(showInfoLevelErrPopUp);
														}, 3000);

														Mousetrap.bind('ctrl+alt+c', function (e) {
															var myVar = setInterval(function () {
																SPKManage.setCtrlAltCFlag(true);
																SPKManage.sendStatusInfoErrorButContinue(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no);
																//SPKManage.setCtrlAltCFlag(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji,true);
																hideSpkElementModal('infoLevelError', true);
																document.getElementById("questionMsg").innerHTML = '';
																mgr.reloadPreviousPage(name_next_exam2);
																clearInterval(myVar);
																return false;
															}, 3000);
														});
														SRState.popup_show_flag = SRState.popup_show_flag + 1;

													} else {
														mgr.reloadPreviousPage(name_next_exam2);
														return false;
													}
												}//end of answer merge exception check in VLM 	
											}));  //End of Volume Level Meter Next Button On Click


											SPKManage.writeLog(`>>>>>>>>>>>>>> next button clicked`);
											//$next_button.attr("display","disabled");
											$next_button.off('click');
											SPKManage.writeLog(`NextButtton double click off`);
											SRState.prev_page_name = SRState.curPageName;
											needs_set_resume = true;
											SPKManage.stopRecordSound(100);
											SPKManage.stopMicCheck();
											timer1.pause();
											terminated = true;
											SPKManage.writeLog("$next_button showRecordingVolumeLevelError:" + showRecordingVolumeLevelError);
											//added By Vijaya EIK-290 start	

											if (showRecordingVolumeLevelError/* && SRState.VLMError_popup_display==false*/) {
												SPKManage.writeLog("inside if showRecordingVolumeLevelError: " + showRecordingVolumeLevelError);
												showSpkBodyModal('volError');
												SPKManage.writeLog("inside if end " + showRecordingVolumeLevelError);
											} else {

												//added By Vijaya EIK-290 end
												if (!SPKManage.saveRecordedSound(wavPathNext)) {
													SPKManage.writeLog(`convert mp3 process result: saveRecordedSound failed 004`);
													throw new Error('音声の保存に失敗しました');
												}

												SPKManage.writeLog(`next button click. loading next page ${name_next_exam}` + interview_id$1);

												//var status_code = "S010"; //Added by Vijaya EIK-18 -> Question End(Answered) status
												//var interview_element_part_no = `${interview$1.interview_id}-NA-${interview$1.part_no}`;
												//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, false);//added by Vijaya EIK-18

												/*Added By Sharad For CBT#135 Start */
												if (SRState.is_yes_no_type_page == 1) {
													resume_element_id = "";
													SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
												}
												else {
													SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
												}

												/*Added By Sharad For CBT#135 End */
												SPKManage.writeLog(`convertMP3 $next_button 22 mp3PathNext` + mp3PathNext);
												SPKManage.convertMP3(wavPathNext, mp3PathNext, SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, interview_id$1, element_page_id$1);
												SPKManage.writeLog(`next button click. loading next page ${name_next_exam}`);
												if (!SPKManage.isMeargeException()) {
													try {
														var temp_prev_page_visited = SRState.prev_page_visited - 1;
														SPKManage.writeLog(`Replay click file name:` + interview$1.record_file_name + ` count:` + temp_prev_page_visited);
														if (temp_prev_page_visited > 0) {
															SPKManage.setReplyClickCount(interview_id$1, temp_prev_page_visited, SRState.login_id);
														}
													} catch (e) { }
													//var popupShowFlag = false;
													if (SPKManage != null && SPKManage.isAudioError(SRState.login_id) != null && SRState.popup_show_flag == 0) {

														var interview_element_part_no;

														var status_code = "S017"; //Added by Vijaya EIK-18 -> Info level error occured status
														if (resume_element_id != "") {
															interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
														} else {
															interview_element_part_no = `${interview$1.interview_id}-NA-${interview$1.part_no}`;
														}
														SPKManage.setInfoLevelErrorFlag(true);
														//var interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
														SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, true);//added by Vijaya EIK-18

														var showInfoLevelErrPopUp = setInterval(function () {
															var questionNum = SPKManage.isAudioError(SRState.login_id);

															showSpkElementModal('infoLevelError');
															document.getElementById("questionMsg").innerHTML = "Audio recording info level error occures at " + questionNum + "<br>Please call the administrator.";
															clearInterval(showInfoLevelErrPopUp);
														}, 3000);

														Mousetrap.bind('ctrl+alt+c', function (e) {
															var myVar = setInterval(function () {
																SPKManage.setCtrlAltCFlag(true);
																SPKManage.sendStatusInfoErrorButContinue(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no);
																//SPKManage.setCtrlAltCFlag(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji,true);
																hideSpkElementModal('infoLevelError', true);
																document.getElementById("questionMsg").innerHTML = '';
																mgr.reloadPreviousPage(name_next_exam2);
																clearInterval(myVar);
															}, 3000);
														});
														SRState.popup_show_flag = SRState.popup_show_flag + 1;

													} else {
														mgr.reloadPreviousPage(name_next_exam2);
													}
												}
											} //else of volume level meter check
										}));

									}
								} else {
									var $next_button = jquery$1('#next-button');
									$next_button.hide();
									$next_button.attr("display", "none");
								}

								// 2017/02/01 Added By Abhijeet - #58,#59(Merge answer recordings which are created on replay button) -start
								var ansDirReplay = SPKManage.makeAnsBaseDir();
								var fnameReplay = (interview$2.record_file_name.replace('%login_id%', SRState.login_id)).replace('.mp3', '@@.mp3');
								var wavPathReplay = `cbt/testResults/spkexam/wav/${fnameNext}_${SRState.prev_page_visited}.wav`;
								var mp3PathReplay = `${ansDirReplay}${fnameReplay}`;
								if (element$2.type == 'record') {
									var $replay_button = jquery$1('#replayButton');
									if ($replay_button != null) {
										$replay_button.off('click').on('click', wrap_log(function (event) {
											SPKManage.writeLog('replay button click1.');
											$replay_button.off('click');
											timer1.pause();
											terminated = true;
											needs_set_resume = false;
											SPKManage.stopRecordSound(100);
											SPKManage.stopMicCheck();
											if (SPKManage.saveRecordedSound(wavPathReplay)) {
												SPKManage.writeLog(`convertMP3 $replay_button 33 mp3PathReplay ` + mp3PathReplay);
												SPKManage.convertMP3(wavPathReplay, mp3PathReplay, SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, interview_id$1, element_page_id$1);
											} else {
												SPKManage.writeLog(`convert mp3 process result: saveRecordedSound failed 005`);
												throw new Error('音声の保存に失敗しました');
											}
											if (!SPKManage.isMeargeException()) {
												SRState.prev_page_name = SRState.curPageName;
												SRState.prev_page_visited = SRState.prev_page_visited + 1;
												pageManager.reloadPreviousPage(SRState.prev_page_name);
											}
										}));
									}
								} else {
									var $replay_button = jquery$1('#replayButton');
									if ($replay_button != null) {

										var maxReplayClickCnt = 0;
										if (element$2.max_replay_click_count != null) {
											maxReplayClickCnt = element$2.max_replay_click_count;
										}

										updateReplayButtonVisibility(maxReplayClickCnt);

										$replay_button.off('click').on('click', wrap_log(function (event) {
											SPKManage.writeLog('replay button click2.');
											timer1.pause();
											terminated = true;
											$replay_button.off('click');
											if (!SPKManage.isMeargeException()) {
												SRState.prev_page_name = SRState.curPageName;
												SRState.prev_page_visited = SRState.prev_page_visited + 1;
												pageManager.showPage(SRState.prev_page_name);
												if (is_yes_no_page == 1) {
													//SPKManage.writeLog('element$2.yes_no_type.'+element$2.yes_no_type);
													SRState.reply_button_clicked = true;
												}
											}
										}));
									}
								}
								//2018/01/08 Added by Kailas - #50 (On click of Yes/No button corresponing question will be loaded.) - End

								//2018/01/12 Added By Abhijeet - #126  (Avoid Flickering while loading recording,pause or yesnobutton page ) - End

							} else {
								//2018/02/14 Added by Kailas - #1 (Login using Authentication API) - Start  
								var isMergingCompleted = false;
								document.getElementById("loadingDiv").style.display = "block";
								var mergingInterval = setInterval(function () {
									if (SPKManage.isMeargingCompleted()) {
										if (isMergingCompleted == false) {
											jquery$1('#loadingDiv').css('display', 'none');
											isMergingCompleted = true;

											//var status_code = "S011"; //Added by Vijaya EIK-18 -> Answer merge completed status 
											//var interview_element_part_no = `${interview$1.interview_id}-${element$1.element_page_id}-${interview$1.part_no}`;
											//var interview_element_part_no = `${interview$1.interview_id}-NA-${interview$1.part_no}`;
											//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, false);//added by Vijaya EIK-18

											SPKManage.writeLog(`Merging Completed. Calling LoginAPI Start`);
											SPKManage.saveExamEndDate(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji);
											SPKManage.validateLogin(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji);
											SPKManage.writeLog(`Merging Completed. Calling LoginAPI End. Loading next page. : ` + name_next_exam);
											mgr.showPage(name_next_exam);
										} else {
											clearInterval(mergingInterval);
											SPKManage.writeLog(`Interval Cleared.`);
										}
									}
								}, 2000);
								//2018/02/14 Added by Kailas - #1 (Login using Authentication API) - End  						
							}

						}));
					}
					play(callSiteLabel) {
						srvideo_play_call_count += 1;
						var count = srvideo_play_call_count;
						var label = callSiteLabel || 'SRVideo.play:unknown';
						var this$1 = this;
						SPKManage.writeLog(`start playing: ${this.fname} callCount=${count} callSite=${label} videoFlowId=${this._videoFlowId}`);
						this.$video.each(function (idx, elem) {
							var flowMeta = elem._videoFlowMeta || {};
							var flowExtra = {
								callCount: count,
								callSite: label,
								fname: this$1.fname,
								videoIndex: idx,
								videoFlowId: flowMeta.videoFlowId || this$1._videoFlowId,
								generation: flowMeta.getGeneration ? flowMeta.getGeneration() : this$1._generation,
								reinitCount: flowMeta.getReinitCount ? flowMeta.getReinitCount() : this$1._reinitCount
							};
							if (elem._videoError4RecoverSkipSrvideoPlay) {
								elem._videoError4RecoverSkipSrvideoPlay = false;
								logMedia(elem, 'SRVideo.play:skipped', Object.assign({}, flowExtra, {
									reason: 'gate5vPassedAfterError4Recover'
								}));
								return;
							}
							setTimeout(function () {
								logMedia(elem, 'SRVideo.play:before', flowExtra);
								if (this$1._nativeMode) {
									syncNativeVideoBounds(elem);
									applyNativeVideoVolumeFromState();
									SPKManage.playNativeVideo();
									logMedia(elem, 'SRVideo.play:resolved', flowExtra);
									return;
								}
								var promise = elem.play();
								if (promise && promise.then) {
									promise.then(function () {
										logMedia(elem, 'SRVideo.play:resolved', flowExtra);
										setTimeout(function () {
											var sample = null;
											try { sample = sampleVideoPixels(elem); } catch (e) { sample = { error: String(e) }; }
											logMedia(elem, 'SRVideo.play:500ms', Object.assign({}, flowExtra, { sample: sample }));
										}, 500);
									}).catch(function (err) {
										logMedia(elem, 'SRVideo.play:rejected', Object.assign({}, flowExtra, {
											rejectName: err && err.name,
											rejectMessage: err && err.message
										}));
									});
								}
							}, 500);
						});
					}
					pause() {
						SPKManage.writeLog(`pause playing: ${this.fname}`);
						if (this._nativeMode) {
							SPKManage.pauseNativeVideo();
							return;
						}
						this.$video.each(function (idx, elem) {
							elem.pause();
						});
					}

				}

				var video = null;
				if (audio_filename) {
					SPKManage.writeLog('[video-state] ' + JSON.stringify({
						label: 'SRVideo:new',
						elementType: element$1.type,
						interviewId: interview$1.interview_id,
						elementPageId: element$1.element_page_id,
						audioFilename: audio_filename,
						decryptedPath: decrypted_dir + audio_filename,
						nativeVideo: isNativeVideoBridgeEnabled()
					}));
					video = new SRVideo(jquery$1('#js-exam-video'), audio_filename, decrypted_dir + audio_filename, {
						play() {
							if (element$1.type == 'playback') {
								enable_pause = true;
							}
							SPKManage.writeLog(`SRVideo play()`);

							//var status_code = "S009"; //Added by Vijaya EIK-18 -> Question start status 
							//var interview_element_part_no = `${interview$1.interview_id}-${element$1.element_page_id}-${interview$1.part_no}`;
							//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, false);//added by Vijaya EIK-18

						},
						ended() {
							if (element$1.type == 'playback') {
								enable_pause = false;
							}
							SPKManage.writeLog(`SRVideo ended()`);
						}
					});
				}
				//2018/01/08 Added by Kailas - #50 (On click of Yes/No button corresponing question will be loaded.) - Start
				if (element$1.yes_no_type == "1") {
					is_yes_no_page = 1;
					var $yes_button = jquery$1('#yes-button');
					var $no_button = jquery$1('#no-button');
					if ($yes_button != null) {
						$yes_button.on('click', wrap_log(function (event) {
							buttonYesNoClickFlag = true;
							SRState.is_yes_no_type_page = 1;
							timer.pause();
							terminated = true;
							SRState.reply_button_clicked = false;
							try {
								var temp_prev_page_visited = SRState.prev_page_visited - 1;
								SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + temp_prev_page_visited);
							} catch (e) { }
							SPKManage.writeLog(`yes button click. loading next page ${name_next_exam_yes}`);

							//var status_code = "S012"; //Added by Vijaya EIK-18 -> Yes button click status 
							//var interview_element_part_no = `${interview$1.interview_id}-${element$1.element_page_id}-${interview$1.part_no}`;

							//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, false);//added by Vijaya EIK-18
							//2018/01/10 Modified by Kailas - #164 ("Yes / No" type of the question may have multiple rows. So handle it accordingly.) - Start
							// on click of yes button should load first element of name_next_exam_yes array.
							mgr.showPage(name_next_exam_yes[0]);
							//2018/01/10 Added/Modified by Kailas - #164 ("Yes / No" type of the question may have multiple rows. So handle it accordingly.) - End
						}));
					}
					if ($no_button != null) {
						$no_button.on('click', wrap_log(function (event) {
							buttonYesNoClickFlag = true;
							SRState.is_yes_no_type_page = 1;
							timer.pause();
							terminated = true;
							SRState.reply_button_clicked = false;
							try {
								var temp_prev_page_visited = SRState.prev_page_visited - 1;
								SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + temp_prev_page_visited);
							} catch (e) { }
							SPKManage.writeLog(`no button click. loading next page ${name_next_exam_no}`);

							//var status_code = "S013"; //Added by Vijaya EIK-18 -> Yes button click status 
							//var interview_element_part_no = `${interview$1.interview_id}-${element$1.element_page_id}-${interview$1.part_no}`;

							//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, false);//added by Vijaya EIK-18
							//2018/01/10 Modified by Kailas - #164 ("Yes / No" type of the question may have multiple rows. So handle it accordingly.) - Start
							// on click of no button should load first element of name_next_exam_no array.
							mgr.showPage(name_next_exam_no[0]);
							//2018/01/10 Modified by Kailas - #164 ("Yes / No" type of the question may have multiple rows. So handle it accordingly.) - End
						}));
					}
				}
				//2018/01/08 Added by Kailas - #50 (On click of Yes/No button corresponing question will be loaded.) - End

				var needs_recording = (element$1.type == 'record')
					&& interview$1.is_recording
					&& interview$1.record_file_name;
				var auto_transition = !(element$1.type == 'playback' && element$1.duration == 0);

				// timer
				var timer;
				if (needs_recording) {
					timer = new RecordTimer(element$1.duration);
				} else {
					timer = new CountDownTimer(element$1.duration);
				}

				//var $not_recording = jquery$1('#js-not-recording');
				var $now_recording = jquery$1('#js-now-recording');

				var recordingThresholdMax = SPKManage.getRecordingThresholdMax();
				var recordingThresholdMin = SPKManage.getRecordingThresholdMin();
				var showRecordingVolumeLevelError = true; //added by Vijaya EIK-290
				//var minRecordLevelThreshold = SPKManage.getMinRecordLevelThreshold(); //added By Vijaya EIK-290

				var popUpDisplayed = false; //added by Vijaya EIK-290

				var recordingThresholdCheckingDuration = SPKManage.getRecordingThresholdCheckingDuration(); // in milliseconds
				//2018/11/28 Added by Sharad - #CBTIBA-84 (Set Next Button Display Timing Question Page Wise.) - Start
				if (element$1.hasOwnProperty('next_button_display_timing')) {
					SPKManage.writeLog(`next_button_display_timing present in element$1: ${interview_id$1}-${element_page_id$1}`);
					if (element$1.next_button_display_timing != null && element$1.next_button_display_timing != "") {
						recordingThresholdCheckingDuration = element$1.next_button_display_timing;
					}
				}
				else {
					SPKManage.writeLog(`next_button_display_timing present in element$1: ${interview_id$1}-${element_page_id$1}`);
				}
				//2018/11/28 Added by Sharad - #CBTIBA-84 (Set Next Button Display Timing Question Page Wise.) - End

				var isCheckRecordingThreshold = false;
				var isRecordLevelBelowRecordingThreshold = false;
				var recordLevelBelowRecordingThresholdStartMilliSecond = 0;
				var isMessageShown = false;
				var checklastMilliSecondCount = 0;

				//on load of screen hide click_next_button message.
				var $click_next_message = jquery$1('#click_next_message');
				$click_next_message.hide();
				var $next_button_control_with_msg = jquery$1('#next-button');
				$next_button_control_with_msg.hide();
				SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> $click_next_message.hide();");

				timer.onTick(wrap_log(function () {
					var sec = timer.getSecond();

					if (element$1.duration && timer.isStarted()) {
						var totalDuration = parseInt(element$1.duration);
						var timerText = timer.text();
						SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> timerText: " + timerText);
						//Added By Sharad CBTIBA#86 Set Priority To Reply Button Click Problem  -- START
						/*if(timerText=="00 : 01")
						{
							checklastMilliSecondCount=checklastMilliSecondCount+1;
							SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> checklastMilliSecondCount: "+checklastMilliSecondCount);
							if(checklastMilliSecondCount==6)
							{	
							var $replay_button = jquery$1('#replayButton');	
							$replay_button.hide();
							}
						}*/
						//Added By Sharad CBTIBA#86 Set Priority To Reply Button Click Problem  -- END
						if (element$1.type == 'record') {
							var milliSecondsToDisableNextBUtton = 1000;
							var secondsToSkipCheckRecordingThreshold = 1;
							SPKManage.setEvnRecordCheckPage(false);
							var recordLevel = SPKManage.getRecordLevel(SRState.rec_volume);

							SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> recordingThresholdMax: " + recordingThresholdMax + "recordingThresholdMin: " + recordingThresholdMin + " recordingThresholdCheckingDuration: " + recordingThresholdCheckingDuration + " recordLevel: " + recordLevel);

							var timerStr = timerText.split(":");
							var timerMinutes = timerStr[0];
							var timerSeconds = timerStr[1];
							// Convert the duration in milliseconds
							var durationInMilliSeconds = ((parseInt(timerMinutes) * 60) + parseInt(timerSeconds)) * 1000;

							//2018/01/08 Modified by Kailas - #53 (disable next button 2 seconds before timer finish.) & #54 (display next button click message) - Start
							//check recording threshold  & next botton disable code will call only if next page available.
							if (name_next_exam != 'EndExam') {
								//check recording threshold only if user speaks, otherwise no message will be displayed.
								/* Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- START
								if(isCheckRecordingThreshold == false && (recordLevel > recordingThresholdMax)){
									isCheckRecordingThreshold = true;
									SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> isCheckRecordingThreshold: "+isCheckRecordingThreshold);
								} 
								
								
								if(isCheckRecordingThreshold == true){
									//show message only if recordLevel <= recordingThreshold.
									if(recordLevel <= recordingThresholdMin){
								
										SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> if(recordLevel <= recordingThresholdMin){ ");
										//set below recording threshold start time.
										if(recordLevelBelowRecordingThresholdStartMilliSecond == 0){
											recordLevelBelowRecordingThresholdStartMilliSecond = durationInMilliSeconds;
											SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> if(recordLevelBelowRecordingThresholdStartMilliSecond == 0){ recordLevelBelowRecordingThresholdStartMilliSecond: "+recordLevelBelowRecordingThresholdStartMilliSecond);
										}else
										Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- END
									   */
								if (recordLevelBelowRecordingThresholdStartMilliSecond == 0) {
									recordLevelBelowRecordingThresholdStartMilliSecond = durationInMilliSeconds;
								}

								SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> recordLevelBelowRecordingThresholdStartMilliSecond" + recordLevelBelowRecordingThresholdStartMilliSecond + " ::durationInMilliSeconds:" + durationInMilliSeconds);
								if ((isMessageShown == false) && ((recordLevelBelowRecordingThresholdStartMilliSecond - durationInMilliSeconds) >= recordingThresholdCheckingDuration) && recordingThresholdCheckingDuration != "-1") { // conditon for which message will be shown.
									SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> (durationInMilliSeconds>= recordingThresholdCheckingDuration) && recordingThresholdCheckingDuration!=-1");
									var $click_next_message = jquery$1('#click_next_message');
									$click_next_message.show();
									var $next_button_control_with_msg = jquery$1('#next-button');
									$next_button_control_with_msg.show();
									SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> $click_next_message.show();");
									isMessageShown = true;
								}

								//added by vijaya EIK-290 start												
								if (recordLevel > recordingThresholdMax) {
									SPKManage.writeLog("In if recordLevel: " + recordLevel + "recordingThresholdMax : " + recordingThresholdMax);
									SPKManage.writeLog("before showRecordingVolumeLevelError set: " + showRecordingVolumeLevelError);
									showRecordingVolumeLevelError = false;
									SPKManage.writeLog("after showRecordingVolumeLevelError set: " + showRecordingVolumeLevelError);
								}
								//added by vijaya EIK-290 end



								/*
								 *Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- END		
									}else{
											//reset below recording threshold start time to zero.
											recordLevelBelowRecordingThresholdStartMilliSecond = 0;
											SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> else recordLevelBelowLevelStartSecond: "+recordLevelBelowRecordingThresholdStartMilliSecond);
										}
									}
									 Commentted code for RecordingThreshold logic remove - Changed By Abhijeet 2018-12-05  -- CBTIBA#84  -- END
									*/
								SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> durationInMilliSeconds: " + durationInMilliSeconds);
								if (durationInMilliSeconds == milliSecondsToDisableNextBUtton) {
									var $next_button = jquery$1('#next-button');
									if ($next_button != null) {
										SPKManage.writeLog(">>>>>>>>>>>>>>>>>>>>> next-button disabled.");
										//$next_button.attr("disabled","disabled");
										$next_button.off('click');
									}
									var $replay_button1 = jquery$1('#replayButton');
									if ($replay_button1 != null) {
										$replay_button1.hide();
									}
								}
							}
							//2018/01/08 Modified by Kailas - #53 (disable next button 2 seconds before timer finish.) & #54 (display next button click message) - End
						}
						jquery$1('#js-countdown').text(timerText);
					}

					if (SPKManage.getRecordingProgress() > 0.0) {
						// リプレイボタン表示（録音開始後のみ）
						var maxReplayClickCnt = 0;
						if (element$2.max_replay_click_count != null) {
							maxReplayClickCnt = element$2.max_replay_click_count;
						}
						updateReplayButtonVisibility(maxReplayClickCnt);
						//$not_recording.hide();
						$now_recording.show();
					} else {
						//$not_recording.show();
						$now_recording.hide();
					}



					if (sec == 0) {
						timer.stop();
					}

					return (sec != 0)
				}));


				var ansDirNext = SPKManage.makeAnsBaseDir();
				var fnameNext = (interview$1.record_file_name.replace('%login_id%', SRState.login_id)).replace('.mp3', '##.mp3');
				var wavPathNext = `cbt/testResults/spkexam/wav/${fnameNext}_${SRState.prev_page_visited}.wav`;
				var mp3PathNext = `${ansDirNext}${fnameNext}`;

				if (element$1.type == 'record') {
					var $next_button = jquery$1('#next-button');
					if ($next_button != null) {
						$next_button.on('click', wrap_log(function (event) {
							SPKManage.writeLog(`>>>>>>>>>>>>>> next button clicked.`);
							//$next_button.attr("display","disabled");
							$next_button.off('click');
							SPKManage.stopRecordSound(100);
							SPKManage.stopMicCheck();
							SPKManage.writeLog("$next_button showRecordingVolumeLevelError:" + showRecordingVolumeLevelError);
							//added By Vijaya EIK-290 start
							if (showRecordingVolumeLevelError) {
								var showRecordingVolumeLevelErrPopUp = setInterval(function () {
									showSpkElementModal('recordingVolumeLevelError');
									document.getElementById("recordLevelMsg").innerHTML = "Record Level is less than min threshold value. <br> So recording not done, still you want to continue the exam then click Ok.	<br> else click alt+ctrl+w .";
									clearInterval(showRecordingVolumeLevelErrPopUp);
								}, 3000);

								/*Mousetrap.bind('ctrl+alt+w', function(e) {
										  var myVar = setInterval(function(){
											
											 document.getElementById("recordingVolumeLevelError").style.visibility='hidden'; 
											 document.getElementById("recordLevelMsg").innerHTML = '';
											 mgr.showPage(name_next_exam2);
											 clearInterval(myVar);
										  },3000); 
										});*/
							}/*else{
								var removeRecordingVolumeLevelErrPopUp = setInterval(function(){
				    				  document.getElementById("recordingVolumeLevelError").style.visibility='hidden';		            		 
				            		  document.getElementById("recordLevelMsg").innerHTML = "";
				        			  clearInterval(removeRecordingVolumeLevelErrPopUp);
				        		  },3000);						
							}*/
							//added By Vijaya EIK-290 end

							var saved = SPKManage.saveRecordedSound(wavPathNext);
							timer.pause();
							terminated = true;
							if (!saved) {
								SPKManage.writeLog(`convert mp3 process result: saveRecordedSound failed 006`);
								throw new Error('音声の保存に失敗しました');
							}

							/*Added By Sharad For CBT#135 Start */
							if (SRState.is_yes_no_type_page == 1) {
								resume_element_id = "";
								SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
							}
							else {
								SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
							}

							/*Added By Sharad For CBT#135 End */
							SPKManage.writeLog(`convertMP3 $next_button 44 mp3PathNext ` + mp3PathNext);
							SPKManage.convertMP3(wavPathNext, mp3PathNext, SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, interview_id$1, element_page_id$1);
							SPKManage.writeLog(`next button click. loading next page ${name_next_exam}`);
							if (!SPKManage.isMeargeException()) {
								try {
									var temp_prev_page_visited = SRState.prev_page_visited - 1;
									SPKManage.writeLog(`Replay click file name:` + interview$1.record_file_name + ` count:` + temp_prev_page_visited);
									if (temp_prev_page_visited > 0) {
										SPKManage.setReplyClickCount(interview_id$1, temp_prev_page_visited, SRState.login_id);
									}
								} catch (e) { }
								if (SPKManage != null && SPKManage.isAudioError(SRState.login_id) != null && SRState.popup_show_flag == 0) {
									var questionNum = SPKManage.isAudioError(SRState.login_id);

									var interview_element_part_no;

									var status_code = "S017"; //Added by Vijaya EIK-18 -> Info level error occured status
									if (resume_element_id != "") {
										interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
									} else {
										interview_element_part_no = `${interview$1.interview_id}-NA-${interview$1.part_no}`;
									}
									SPKManage.setInfoLevelErrorFlag(true);
									//var interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
									SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, true);//added by Vijaya EIK-18

									var showInfoLevelErrPopUp = setInterval(function () {
										showSpkElementModal('infoLevelError');
										document.getElementById("questionMsg").innerHTML = "Audio recording info level error occures at " + questionNum + ".<br>Please call the administrator.";
										clearInterval(showInfoLevelErrPopUp);
									}, 3000);

									Mousetrap.bind('ctrl+alt+c', function (e) {
										var myVar = setInterval(function () {
											SPKManage.setCtrlAltCFlag(true);
											SPKManage.sendStatusInfoErrorButContinue(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no);
											//SPKManage.setCtrlAltCFlag(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji,true);
											hideSpkElementModal('infoLevelError', true);
											document.getElementById("questionMsg").innerHTML = '';
											mgr.reloadPreviousPage(name_next_exam);
											clearInterval(myVar);
										}, 3000);
									});

									SRState.popup_show_flag = SRState.popup_show_flag + 1;

								} else {
									mgr.reloadPreviousPage(name_next_exam);
								}
								//mgr.reloadPreviousPage(name_next_exam);
							}
						}));
					}
				}

				// 2017/02/01 Added By Abhijeet - #58,#59(Merge answer recordings which are created on replay button) -start
				var ansDirReplay = SPKManage.makeAnsBaseDir();
				var fnameReplay = (interview$1.record_file_name.replace('%login_id%', SRState.login_id)).replace('.mp3', '@@.mp3');
				var wavPathReplay = `cbt/testResults/spkexam/wav/${fnameNext}_${SRState.prev_page_visited}.wav`;
				var mp3PathReplay = `${ansDirReplay}${fnameReplay}`;
				if (element$1.type == 'record') {
					var $replay_button = jquery$1('#replayButton');
					if ($replay_button != null) {
						$replay_button.off('click').on('click', wrap_log(function (event) {
							SPKManage.writeLog('replay button click3.');
							timer.pause();
							terminated = true;
							$replay_button.off('click');
							needs_set_resume = false;

							SPKManage.stopRecordSound(100);
							SPKManage.stopMicCheck();
							if (SPKManage.saveRecordedSound(wavPathReplay)) {
								SPKManage.writeLog(`convertMP3 $replay_button 55 mp3PathReplay ` + mp3PathReplay);
								SPKManage.convertMP3(wavPathReplay, mp3PathReplay, SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, interview_id$1, element_page_id$1);
							} else {
								SPKManage.writeLog(`convert mp3 process result: saveRecordedSound failed 007`);
								throw new Error('音声の保存に失敗しました');
							}
							if (!SPKManage.isMeargeException()) {
								//SRState.prev_page_visited=SRState.prev_page_visited+1;
								pageManager.reloadPreviousPage(SRState.prev_page_name);
							}
						}));
					}


				} else {
					var $replay_button = jquery$1('#replayButton');
					if ($replay_button != null) {
						$replay_button.off('click').on('click', wrap_log(function (event) {
							SPKManage.writeLog('replay button click4.');
							timer.pause();
							terminated = true;
							$replay_button.off('click');
							if (!SPKManage.isMeargeException()) {
								if (is_yes_no_page == 1) {
									SRState.reply_button_clicked = true;
									SPKManage.writeLog('YES NO button hidden.');
								}
								pageManager.showPage(SRState.prev_page_name);
							}
						}));
					}

				}
				// 2017/02/01 Added By Abhijeet - #58,#59(Merge answer recordings which are created on replay button) -end

				timer.onStop(wrap_log(function () {
					enable_pause = false;
					if (needs_recording) {
						var ended_alarm = false;
						var ended_conv_mp3 = false;

						if (element$1.alarm_sound) {
							jquery$1('#js-end-beep').get(0).play();
							jquery$1('#js-end-beep').on('ended', wrap_log(function (event) {
								ended_alarm = true;
							}));
						} else {
							ended_alarm = true;
						}

						//var status_code = "S010"; //Added by Vijaya EIK-18 -> Question End(Answered) status
						//var interview_element_part_no = `${interview$1.interview_id}-NA-${interview$1.part_no}`;
						//SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, false);//added by Vijaya EIK-18

						var ansDir = SPKManage.makeAnsBaseDir();
						var fname = interview$1.record_file_name.replace('%login_id%', SRState.login_id);
						var wavPath = `cbt/testResults/spkexam/wav/${fname}_${SRState.prev_page_visited}.wav`;
						var mp3Path = `${ansDir}${fname}`;

						/*Added By Sharad For CBT#135 Start */
						if (SRState.is_yes_no_page == 1) {
							resume_element_id = "";
							SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
						}
						else {
							SPKManage.addQuestionInterviewIdInStateFile(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, resume_element_id);
						}
						/*Added By Sharad For CBT#135 End */
						setTimeout(wrap_log(function () {
							if (SPKManage.saveRecordedSound(wavPath)) {
								SPKManage.writeLog(`convertMP3 timer.onStop 66 mp3Path.replace('.mp3','##.mp3') ` + mp3Path.replace('.mp3', '##.mp3'));
								SPKManage.convertMP3(wavPath, mp3Path.replace('.mp3', '##.mp3'), SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji, interview_id$1, element_page_id$1);
							}

							var conv_proc = function () {
								if (!SPKManage.isConvertingMP3()) {
									ended_conv_mp3 = true;
									return
								}
								setTimeout(wrap_log(conv_proc), 100);
							};
							conv_proc();
						}), 10);

						var wait_proc = function () {
							if (ended_conv_mp3 && ended_alarm) {
								var exit_value = SPKManage.getConvMP3Result();
								if (exit_value != 0) {
									SPKManage.writeLog(`convert mp3 process result: ${exit_value}`);
									throw new Error('音声の保存に失敗しました')
								}

								//checkMP3FileSize(mp3Path);
								try {
									var temp_prev_page_visited = SRState.prev_page_visited - 1;
									SPKManage.writeLog(`Replay click file name:` + interview$1.record_file_name + ` count:` + temp_prev_page_visited);
									if (temp_prev_page_visited > 0) {
										SPKManage.setReplyClickCount(interview_id$1, temp_prev_page_visited, SRState.login_id);
									}
								} catch (e) { }
								if (SPKManage != null && SPKManage.isAudioError(SRState.login_id) != null && SRState.popup_show_flag == 0) {
									var questionNum = SPKManage.isAudioError(SRState.login_id);

									var interview_element_part_no;

									var status_code = "S017"; //Added by Vijaya EIK-18 -> Info level error occured status
									if (resume_element_id != "") {
										interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
									} else {
										interview_element_part_no = `${interview$1.interview_id}-NA-${interview$1.part_no}`;
									}
									SPKManage.setInfoLevelErrorFlag(true);
									//var interview_element_part_no = `${interview$1.interview_id}-${resume_element_id}-${interview$1.part_no}`;
									SPKManage.saveExamStatus(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no, status_code, true);//added by Vijaya EIK-18

									var showInfoLevelErrPopUp = setInterval(function () {
										showSpkElementModal('infoLevelError');
										document.getElementById("questionMsg").innerHTML = "Audio recording info level error occures at " + questionNum + ".<br>Please call the administrator.";
										clearInterval(showInfoLevelErrPopUp);
									}, 3000);

									Mousetrap.bind('ctrl+alt+c', function (e) {
										var myVar = setInterval(function () {
											SPKManage.setCtrlAltCFlag(true);
											SPKManage.sendStatusInfoErrorButContinue(SRState.login_id, SRState.curTestSetId, SRState.year, SRState.kaiji, interview_element_part_no);
											//SPKManage.setCtrlAltCFlag(SRState.login_id, SRState.curTestSetId, interview_id_prev$1, SRState.apply_id, SRState.year, SRState.kaiji,true);
											hideSpkElementModal('infoLevelError', true);
											document.getElementById("questionMsg").innerHTML = '';
											mgr.showPage(name_next_exam);
											clearInterval(myVar);
										}, 3000);
									});

									SRState.popup_show_flag = SRState.popup_show_flag + 1;

								} else {
									mgr.showPage(name_next_exam);
								}
								//mgr.showPage(name_next_exam);
								return
							}
							setTimeout(wrap_log(wait_proc), 100);
						};
						wait_proc();
					} else {
						if (element$1.alarm_sound) {
							jquery$1('#js-end-beep').get(0).play();
							jquery$1('#js-end-beep').on('ended', wrap_log(function (event) {
								if (auto_transition) {
									if (buttonYesNoClickFlag == false) {
										//SPKManage.writeLog(`Yes / No button is not selected. Calling showPage() from onStop`);
										SRState.yes_no_page_map[SRState.curPageName] = SRState.yes_no_page_map[SRState.curPageName] == null ? 1 : SRState.yes_no_page_map[SRState.curPageName] + 1;
										if (element$1.yes_no_type == "1" && (SRState.yes_no_page_map[SRState.curPageName] == null || SRState.yes_no_page_map[SRState.curPageName] <= 1)) {
											//SRState.prev_page_visited = 1;
											//SRState.prev_page_visited = 0;
											prev_page_visited_temp = SRState.prev_page_visited - 1;
											if (SRState.replay_max_click_count == prev_page_visited_temp && SRState.replay_max_click_count != 0) {
												SRState.reply_button_clicked = false;
												SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
												mgr.showPage(name_next_exam);
											}
											else {
												if (SRState.reply_button_clicked == true) {
													SRState.reply_button_clicked = false;
													SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
													SPKManage.writeLog(`Replay click & timer end thats why moving to next Question`);
													mgr.showPage(name_next_exam);
												}
												else {
													yesNoReplyButtonCheckFlag = true;
													SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
													pageManager.reloadPreviousPage(SRState.curPageName);
												}
											}
										} else {
											SRState.yes_no_page_map[SRState.curPageName] = 0;
											SRState.reply_button_clicked = false;
											mgr.showPage(name_next_exam);
										}
										//mgr.showPage(name_next_exam);
									} else {
										SPKManage.writeLog(`Yes / No button is selected. Skipping showPage() from onStop`);
									}
								}
							}));
						} else {
							if (auto_transition) {
								if (buttonYesNoClickFlag == false) {
									//SPKManage.writeLog(`Yes / No button is not selected. Calling showPage() from onStop`);
									SRState.yes_no_page_map[SRState.curPageName] = SRState.yes_no_page_map[SRState.curPageName] == null ? 1 : SRState.yes_no_page_map[SRState.curPageName] + 1;
									if (element$1.yes_no_type == "1" && (SRState.yes_no_page_map[SRState.curPageName] == null || SRState.yes_no_page_map[SRState.curPageName] <= 1)) {
										//SRState.prev_page_visited = 1;
										//SRState.prev_page_visited = 0;
										prev_page_visited_temp = SRState.prev_page_visited - 1;
										if (SRState.replay_max_click_count == prev_page_visited_temp && SRState.replay_max_click_count != 0) {
											SRState.reply_button_clicked = false;
											SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
											mgr.showPage(name_next_exam);
										}
										else {
											if (SRState.reply_button_clicked == true) {
												SRState.reply_button_clicked = false;
												SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
												SPKManage.writeLog(`Replay click & timer end thats why moving to next Question`);
												mgr.showPage(name_next_exam);
											}
											else {
												yesNoReplyButtonCheckFlag = true;
												SPKManage.writeLog(`Replay click file name of yes/no type :` + interview$1.record_file_name + ` count:` + prev_page_visited_temp);
												pageManager.reloadPreviousPage(SRState.curPageName);
											}
										}
									} else {
										SRState.yes_no_page_map[SRState.curPageName] = 0;
										SRState.reply_button_clicked = false;
										mgr.showPage(name_next_exam);
									}
									//mgr.showPage(name_next_exam);
								} else {
									SPKManage.writeLog(`Yes / No button is selected. Skipping showPage() from onStop`);
								}
							}
						}
					}

				}));

				//
				if (element$1.alarm_sound) {
					jquery$1('#js-start-beep').on('ended', wrap_log(function (event) {
						timer.start();
					})).get(0).play();
				} else {
					timer.start();
				}

				timer.onPause(wrap_log(function () {
					if (video) { video.pause(); }
				}));

				timer.onResume(wrap_log(function () {
					if (video) {
						setTimeout(function () { video.play('timer.onResume'); }, 500);
					}
				}));
				wrap_log(function () {
					var alarm = element.alarm_sound ? playStartBeep() : Promise.resolve();
					return alarm.then(function () {
						timer.start();
						if (auto_transition) {
							enable_pause = true;
						}
					})
				})();

				// 試験中断対応
				class PauseExam {
					constructor() {
						var this$1 = this;

						this.keycombo = 'ctrl+alt+s';
						this.buttonHandler = wrap_log(function () {
							var pw = jquery$1('#stopPassWord').val();
							if (SPKManage.chkAdmPassword(pw)) {
								this$1.leave();
							} else {
								jquery$1('.stoppage .passerror').show();
							}
						});
					}

					bind() {
						var this$1 = this;

						mousetrap.bind(this.keycombo, wrap_log(function (e) {
							this$1.enter();
						}));
					}

					unbind() {
						mousetrap.unbind(this.keycombo);
					}

					enter() {
						if (!enable_pause) {
							return
						}
						this.unbind();
						if (checkType === "pause") {
							timer1.pause();
						}
						else if (checkType === "record") {
							isCheckTypeRecordFreez = SRState.prev_page_visited;
							timer1.pause();
							terminated = true;
						}
						else {
							timer.pause();
						}

						SPKManage.writeLog('Pause exam');

						showSpkBodyModal('stoppage');
						jquery$1('#stopPassWord').val("");
						jquery$1('.stoppage .passerror').hide();
						jquery$1('#js-ret-pause').on('click', this.buttonHandler);
						if (checkType === "record") {
							jquery$1('#js-rerecord-msg').html('<br>音声は再録音となります。');
						}
					}

					leave() {
						SPKManage.writeLog('Resume exam');
						var skipRestore = (checkType === "record");
						hideSpkBodyModal('stoppage', skipRestore);
						jquery$1('#js-ret-pause').off('click', this.buttonHandler);
						this.bind();
						if (checkType === "pause") {
							timer1.resume();
						}
						else if (checkType === "record") {
							mgr.showPage(name_exam);
						}
						else {
							timer.resume();
						}
					}
				}
				var pause_exam = new PauseExam();
				pause_exam.bind();
			},

			pageWillDisappear(mgr) {
				SPKManage.writeLog('>>>>>>>>>>>>>>>>>>>>>> pageWillDisappear(mgr) {');
				SPKManage.writeLog(`page will disappear: ${interview_id$1}-${element_page_id$1}`);

				// 解答済み設定
				if (needs_set_resume && !SPKManage.isMeargeException()) {
					SPKManage.setAnsweredInterviewId(SRState.login_id, SRState.curTestSetId, interview_id$1, SRState.apply_id, SRState.year, SRState.kaiji);
				}
			},
		};

		pageManager.addPage(name_exam, page_exam);
	};

	for (var i$1 = 0; i$1 < exam_pages.length; ++i$1) loop(i$1);

	return exam_pages
}

//
window.terminate_sr = function () {
	jquery$1('video,audio').each(function (i, elem) {
		elem.pause();
	});
};

//
jquery$1(function () {
	jquery$1(document).unbind("dragstart");        // drag & drop禁止
	jquery$1(document).unbind("contextmenu");      // 右クリック禁止

	jquery$1('audio,video').on('loadeddata', wrap_log(function (event) {
		event.target.volume = 1;
		event.target.volume = 0;
		event.target.volume = SRState.volume / 100;
	}));

	var bootFunc = wrap_log(function () {
		installNativeVideoGlobalBridge();
		if (!window.hasOwnProperty('SPKManage')) {
			setTimeout(bootFunc, 10);
			return
		}

		Object.assign(SRState, getState());

		window.pageManager = new PageManager();

		pageManager.addPage('Login', PageLogin);
		pageManager.addPage('Description1', PageDescription1);
		//pageManager.addPage('StepGuidePartOne', PageStepGuidePartOne);
		pageManager.addPage('TestBeforeCheck', PageTestBeforeCheck);
		pageManager.addPage('SoundCheck', PageSoundCheck);
		//pageManager.addPage('MicCheck', PageMicCheck);
		pageManager.addPage('RecordCheck', PageRecordCheck);
		//pageManager.addPage('StepGuidePartTwo', PageStepGuidePartTwo);
		//pageManager.addPage('StepGuidePartThree', PageStepGuidePartThree);
		pageManager.addPage('Description2', PageDescription2);
		pageManager.addPage('EndExam', PageEndExam);
		pageManager.addPage('TermState', PageTermState);

		// 問題画面作成
		if (SRState['curTestSetId']) {
			createExamPages(pageManager);
		}

		var curPageName = SRState.curPageName;
		if (curPageName) {
			pageManager.resumePage(curPageName);
		} else {
			pageManager.showPage(SPKManage.getStartPageName());
		}
	});

	bootFunc();
});

/*Function Called to hide Replay Button
 * Changed By- Abhijeet 2017/12/14
 * TicketNo. CBT-55
 */
function getReplayButtonElement() {
	return document.getElementById('replayButton');
}

function hideReplayButton() {
	var replay = getReplayButtonElement();
	if (replay) {
		replay.style.display = 'none';
	}
	jquery$1('#replayButton').hide();
}

function shouldShowReplayButton(maxReplayClickCnt) {
	var limit = maxReplayClickCnt || 0;
	if (limit <= 0) {
		return false;
	}
	return SRState.prev_page_visited <= limit;
}

function updateReplayButtonVisibility(maxReplayClickCnt) {
	var $replay_button = jquery$1('#replayButton');
	if (!$replay_button.length) {
		return;
	}
	if (shouldShowReplayButton(maxReplayClickCnt)) {
		$replay_button.show();
		SPKManage.writeLog('Replay button visiable. ReplayCnt [' + SRState.prev_page_visited + ']');
	} else {
		$replay_button.hide();
	}
}

function whenVideoPlaying() {
	hideReplayButton();
}

/*Function Called when video is ready to play (canplay / native ready)
 * 画面描画・動画読み込み中は非表示のままにする。
 * Changed By- Abhijeet 2017/12/14, updated 2026-06-29
 * TicketNo. CBT-55
 */
function whenVideoLoaded() {
	hideReplayButton();
}

/*Function Called Load Previous Page on Replay Button Click
 * Changed By- Abhijeet 2017/12/14
 * TicketNo. CBT-55
 */
function playVideoAgain() {
	pageManager.showPage(SRState.prev_page_name);
}